@crustjs/skills 0.1.0 → 0.1.1

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.
Files changed (2) hide show
  1. package/dist/index.js +15 -15
  2. package/package.json +5 -4
package/dist/index.js CHANGED
@@ -1,16 +1,16 @@
1
1
  // @bun
2
- import{accessSync as TX,constants as _X,statSync as OX}from"fs";import{homedir as JQ}from"os";import{delimiter as jX,join as J}from"path";var h=J(".agents","skills"),bX=J(".crust","skills");function PQ(Q){if(Q!==JQ())return J(Q,".config");let X=process.env.XDG_CONFIG_HOME?.trim();return X&&X.length>0?X:J(Q,".config")}function f(Q){return J(Q,".agents","skills")}function EX(Q){return J(Q,".crust","skills")}function u(Q){return Q==="project"&&process.cwd()===JQ()?"global":Q}var s={amp:{label:"Amp",class:"universal",projectSkillsDir:h,globalSkillsDir:f},adal:{label:"AdaL",class:"additional",projectSkillsDir:J(".adal","skills"),globalSkillsDir:(Q)=>J(Q,".adal","skills"),detectCommands:["adal"]},antigravity:{label:"Antigravity",class:"additional",projectSkillsDir:J(".agent","skills"),globalSkillsDir:(Q)=>J(Q,".gemini","antigravity","skills"),detectCommands:["antigravity"]},augment:{label:"Augment",class:"additional",projectSkillsDir:J(".augment","skills"),globalSkillsDir:(Q)=>J(Q,".augment","skills"),detectCommands:["augment"]},"claude-code":{label:"Claude Code",class:"additional",projectSkillsDir:J(".claude","skills"),globalSkillsDir:(Q)=>J(process.env.CLAUDE_CONFIG_DIR?.trim()||J(Q,".claude"),"skills"),detectCommands:["claude","claude-code"]},cline:{label:"Cline",class:"universal",projectSkillsDir:h,globalSkillsDir:f},codebuddy:{label:"CodeBuddy",class:"additional",projectSkillsDir:J(".codebuddy","skills"),globalSkillsDir:(Q)=>J(Q,".codebuddy","skills"),detectCommands:["codebuddy"]},codex:{label:"Codex",class:"universal",projectSkillsDir:h,globalSkillsDir:f},"command-code":{label:"Command Code",class:"additional",projectSkillsDir:J(".commandcode","skills"),globalSkillsDir:(Q)=>J(Q,".commandcode","skills"),detectCommands:["command-code","commandcode"]},continue:{label:"Continue",class:"additional",projectSkillsDir:J(".continue","skills"),globalSkillsDir:(Q)=>J(Q,".continue","skills"),detectCommands:["continue"]},cortex:{label:"Cortex Code",class:"additional",projectSkillsDir:J(".cortex","skills"),globalSkillsDir:(Q)=>J(Q,".snowflake","cortex","skills"),detectCommands:["cortex"]},crush:{label:"Crush",class:"additional",projectSkillsDir:J(".crush","skills"),globalSkillsDir:(Q)=>J(PQ(Q),"crush","skills"),detectCommands:["crush"]},cursor:{label:"Cursor",class:"universal",projectSkillsDir:h,globalSkillsDir:f},droid:{label:"Droid",class:"additional",projectSkillsDir:J(".factory","skills"),globalSkillsDir:(Q)=>J(Q,".factory","skills"),detectCommands:["droid"]},"gemini-cli":{label:"Gemini CLI",class:"universal",projectSkillsDir:h,globalSkillsDir:f},"github-copilot":{label:"GitHub Copilot",class:"universal",projectSkillsDir:h,globalSkillsDir:f},goose:{label:"Goose",class:"additional",projectSkillsDir:J(".goose","skills"),globalSkillsDir:(Q)=>J(PQ(Q),"goose","skills"),detectCommands:["goose"]},"iflow-cli":{label:"iFlow CLI",class:"additional",projectSkillsDir:J(".iflow","skills"),globalSkillsDir:(Q)=>J(Q,".iflow","skills"),detectCommands:["iflow","iflow-cli"]},junie:{label:"Junie",class:"additional",projectSkillsDir:J(".junie","skills"),globalSkillsDir:(Q)=>J(Q,".junie","skills"),detectCommands:["junie"]},kilo:{label:"Kilo Code",class:"additional",projectSkillsDir:J(".kilocode","skills"),globalSkillsDir:(Q)=>J(Q,".kilocode","skills"),detectCommands:["kilo","kilocode"]},"kimi-cli":{label:"Kimi Code CLI",class:"universal",projectSkillsDir:h,globalSkillsDir:f},"kiro-cli":{label:"Kiro CLI",class:"additional",projectSkillsDir:J(".kiro","skills"),globalSkillsDir:(Q)=>J(Q,".kiro","skills"),detectCommands:["kiro","kiro-cli"]},kode:{label:"Kode",class:"additional",projectSkillsDir:J(".kode","skills"),globalSkillsDir:(Q)=>J(Q,".kode","skills"),detectCommands:["kode"]},mcpjam:{label:"MCPJam",class:"additional",projectSkillsDir:J(".mcpjam","skills"),globalSkillsDir:(Q)=>J(Q,".mcpjam","skills"),detectCommands:["mcpjam"]},"mistral-vibe":{label:"Mistral Vibe",class:"additional",projectSkillsDir:J(".vibe","skills"),globalSkillsDir:(Q)=>J(Q,".vibe","skills"),detectCommands:["mistral-vibe","vibe"]},mux:{label:"Mux",class:"additional",projectSkillsDir:J(".mux","skills"),globalSkillsDir:(Q)=>J(Q,".mux","skills"),detectCommands:["mux"]},neovate:{label:"Neovate",class:"additional",projectSkillsDir:J(".neovate","skills"),globalSkillsDir:(Q)=>J(Q,".neovate","skills"),detectCommands:["neovate"]},opencode:{label:"OpenCode",class:"universal",projectSkillsDir:h,globalSkillsDir:f},openclaw:{label:"OpenClaw",class:"additional",projectSkillsDir:"skills",globalSkillsDir:(Q)=>J(Q,".openclaw","skills"),detectCommands:["openclaw"]},openhands:{label:"OpenHands",class:"additional",projectSkillsDir:J(".openhands","skills"),globalSkillsDir:(Q)=>J(Q,".openhands","skills"),detectCommands:["openhands"]},pi:{label:"Pi",class:"additional",projectSkillsDir:J(".pi","skills"),globalSkillsDir:(Q)=>J(Q,".pi","agent","skills"),detectCommands:["pi"]},pochi:{label:"Pochi",class:"additional",projectSkillsDir:J(".pochi","skills"),globalSkillsDir:(Q)=>J(Q,".pochi","skills"),detectCommands:["pochi"]},qoder:{label:"Qoder",class:"additional",projectSkillsDir:J(".qoder","skills"),globalSkillsDir:(Q)=>J(Q,".qoder","skills"),detectCommands:["qoder"]},"qwen-code":{label:"Qwen Code",class:"additional",projectSkillsDir:J(".qwen","skills"),globalSkillsDir:(Q)=>J(Q,".qwen","skills"),detectCommands:["qwen","qwen-code"]},replit:{label:"Replit",class:"universal",projectSkillsDir:h,globalSkillsDir:f},roo:{label:"Roo Code",class:"additional",projectSkillsDir:J(".roo","skills"),globalSkillsDir:(Q)=>J(Q,".roo","skills"),detectCommands:["roo","roo-code"]},trae:{label:"Trae",class:"additional",projectSkillsDir:J(".trae","skills"),globalSkillsDir:(Q)=>J(Q,".trae","skills"),detectCommands:["trae"]},"trae-cn":{label:"Trae CN",class:"additional",projectSkillsDir:J(".trae","skills"),globalSkillsDir:(Q)=>J(Q,".trae-cn","skills"),detectCommands:["trae-cn","trae"]},windsurf:{label:"Windsurf",class:"additional",projectSkillsDir:J(".windsurf","skills"),globalSkillsDir:(Q)=>J(Q,".codeium","windsurf","skills"),detectCommands:["windsurf"]},zencoder:{label:"Zencoder",class:"additional",projectSkillsDir:J(".zencoder","skills"),globalSkillsDir:(Q)=>J(Q,".zencoder","skills"),detectCommands:["zencoder"]}},a=Object.keys(s),A=Object.fromEntries(a.map((Q)=>[Q,s[Q].label]));function S(){return a.filter((Q)=>s[Q].class==="universal")}function m(){return a.filter((Q)=>s[Q].class==="additional")}function RX(Q){return s[Q].class==="universal"}async function n(Q){let X=typeof Q==="string"?{home:Q}:Q??{},Z=X.cwd??process.cwd(),H=X.commandChecker??((W)=>Promise.resolve(IX(W))),$=[];for(let W of m()){let Y=s[W].detectCommands??[],G=!1;for(let x of Y)if(await H(x,Z)){G=!0;break}if(G)$.push(W)}return $}function N(Q,X,Z){let H=u(X),$=s[Q];if(H==="project")return J(process.cwd(),$.projectSkillsDir,Z);return J($.globalSkillsDir(JQ()),Z)}function d(Q,X){if(u(Q)==="project")return J(process.cwd(),bX,X);return J(EX(JQ()),X)}function IX(Q){let Z=(process.env.PATH??"").split(jX).filter((W)=>W.length>0),H=process.platform==="win32",$=H?(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";").filter((W)=>W.length>0):[];for(let W of Z){if(!H&&kQ(J(W,Q)))return!0;if(H){for(let Y of $)if(kQ(J(W,Q+Y)))return!0}}return!1}function kQ(Q){try{if(!OX(Q).isFile())return!1;return TX(Q,_X.X_OK),!0}catch{return!1}}import{Crust as CX}from"@crustjs/core";function zQ(Q){if(Q===void 0)return[];return(Array.isArray(Q)?Q:[Q]).flatMap((Z)=>Z.split(/\r?\n/)).map((Z)=>Z.trim()).filter((Z)=>Z.length>0)}function DQ(Q){let X=Q?.trim();if(!X)return[];return X.split(/\r?\n/)}function VQ(Q){return Q.length>0}var AQ=Symbol("crust.skill.commandAnnotations");function LX(Q){return Q instanceof CX?Q._node:Q}function NX(Q,X){let Z=LX(Q),H=zQ(typeof X==="string"||Array.isArray(X)?X:X.instructions??[]);if(H.length===0)return Q;let $=UQ(Z)?.instructions??[],W=[...new Set([...$,...H])];return Object.defineProperty(Z,AQ,{value:{instructions:W},enumerable:!0,configurable:!0}),Q}function UQ(Q){let X=Q[AQ];if(!X?.instructions||X.instructions.length===0)return;return{instructions:[...X.instructions]}}import{existsSync as $X,statSync as KZ}from"fs";import{readdir as GZ,readFile as WX,realpath as JX,stat as zX}from"fs/promises";import{dirname as YX,isAbsolute as FZ,join as FQ,resolve as GQ,sep as qX}from"path";import{fileURLToPath as xZ}from"url";import{lstat as eX,mkdir as eQ,readlink as QZ,realpath as XZ,rm as qQ,symlink as ZZ,writeFile as lQ}from"fs/promises";import{dirname as QX,join as sQ}from"path";class L extends Error{name="SkillConflictError";details;constructor(Q){super(PX(Q));this.details=Q}}function PX(Q){let X=`Skill conflict for agent "${Q.agent}": directory "${Q.outputDir}"`;if(Q.kindMismatch)return`${X} was installed as a "${Q.kindMismatch.existing}" skill but "${Q.kindMismatch.attempted}" was attempted. Use force: true to overwrite, or uninstall the existing skill first.`;if(Q.manifestMalformed){let{reason:Z,rawKind:H}=Q.manifestMalformed;switch(Z){case"unknown-kind":return`${X} was created by Crust but its crust.json declares an unrecognized kind "${H??"<unknown>"}" \u2014 likely a hand-edit typo or a `+"crust.json written by a newer Crust release. Fix the kind field, upgrade Crust, or pass force: true to overwrite.";case"parse-error":return`${X} contains a crust.json that is not valid JSON. Repair the file, or pass force: true to overwrite the directory.`;case"not-an-object":return`${X} contains a crust.json whose top-level value is not a JSON object. Repair the file, or pass force: true to overwrite the directory.`;case"missing-version":return`${X} contains a crust.json with no "version" string. Repair the file, or pass force: true to overwrite the directory.`}}return`${X} already exists but was not created by Crust (no crust.json found). Delete or rename the conflicting skill to resolve.`}function SQ(Q){return vQ(Q,[])}function vQ(Q,X){let Z=kX(Q.meta.name),H=[...X,Z],$=DX(Q.args),W=SX(Q.effectiveFlags),Y=yX(Q.subCommands,H),G=UQ(Q);return{name:Z,path:H,description:Q.meta.description,usage:Q.meta.usage,instructions:G?.instructions,runnable:typeof Q.run==="function",args:$,flags:W,children:Y}}function kX(Q){return Q.trim().toLowerCase()}function DX(Q){if(!Q||Q.length===0)return[];return Q.map(AX)}function AX(Q){let X={name:Q.name,type:Q.type,required:Q.required===!0,variadic:Q.variadic===!0};if(Q.description!==void 0)X.description=Q.description;if(Q.default!==void 0)X.default=yQ(Q.default);return X}function SX(Q){if(!Q)return[];return Object.keys(Q).sort().map((Z)=>{return vX(Z,Q[Z])})}function vX(Q,X){let Z={name:Q,type:X.type,required:X.required===!0,multiple:X.multiple===!0,short:X.short,aliases:X.aliases?[...X.aliases].sort():[]};if(X.description!==void 0)Z.description=X.description;if(X.default!==void 0)Z.default=yQ(X.default);return Z}function yX(Q,X){return Object.keys(Q).sort().map((H)=>{return vQ(Q[H],X)})}function yQ(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function WQ(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 hQ(Q){return Q.replace(/(?<!\\)\|/g,"\\|")}function fQ(Q,X){let Z=[],H=gQ(Q);Z.push({path:"SKILL.md",content:hX(Q,X,H)});for(let $ of H){let W=i($),Y=$.children.length>0?uX($,Q):pX($,Q);Z.push({path:W,content:Y})}return Z}function gQ(Q){let X=[Q];for(let Z of Q.children)X.push(...gQ(Z));return X}function i(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function MQ(Q){return Q.path.join(" ")}function wQ(Q,X){let Z=Q.split("/").slice(0,-1),H=X.split("/"),$=0;while($<Z.length&&$<H.length&&Z[$]===H[$])$++;let W=Z.length-$,Y=H.slice($);if(W===0)return Y.join("/");return[...Array.from({length:W},()=>".."),...Y].join("/")}function hX(Q,X,Z){let H=[];if(H.push("---"),H.push(`name: ${WQ(X.name)}`),H.push(`description: ${WQ(X.description)}`),X.license)H.push(`license: ${WQ(X.license)}`);if(X.compatibility)H.push(`compatibility: ${WQ(X.compatibility)}`);if(X.disableModelInvocation)H.push("disable-model-invocation: true");if(X.allowedTools)H.push(`allowed-tools: ${WQ(X.allowedTools)}`);if(H.push("metadata:"),H.push(` version: "${X.version}"`),H.push("---"),H.push(""),H.push(`# ${X.name}`),H.push(""),Q.description)H.push(Q.description),H.push("");H.push(`You should use this skill when you need accurate help with \`${X.name}\` commands, including command selection, syntax, arguments, flags, defaults, and subcommands.`),H.push("");let $=oX(X.instructions);if(H.push("## How to Use This Skill"),H.push(""),H.push("1. You must find the command that best matches the user's task from the Command Reference below."),H.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."),H.push("3. You should read only the linked file or files you need from `commands/`."),H.push("4. You must read a command's file before answering a command-specific question or suggesting that command."),H.push("5. You must treat the command file as the source of truth for usage, arguments, flags, aliases, and defaults."),H.push("6. If a flag, argument, alias, or default is not documented there, you must say it is not documented instead of guessing."),H.push(""),VQ($))H.push("## General Guidance"),H.push(""),H.push(...$),H.push("");if(H.push("## Command Reference"),H.push(""),H.push("You should use this table to locate the command file you need."),H.push(""),H.push(...fX(Z)),H.push(""),Q.runnable){H.push("## Usage"),H.push("");let W=i(Q);H.push(`The root command is directly executable. You should see [${Q.name}](${W}) for usage details.`),H.push("")}return H.join(`
3
- `)}function fX(Q){let X=[];X.push("| Command | Type | Documentation |"),X.push("| ------- | ---- | ------------- |");for(let Z of Q){let H=MQ(Z),$=i(Z),W=gX(Z);X.push(`| \`${H}\` | ${W} | [${$}](${$}) |`)}return X}function gX(Q){if(Q.runnable&&Q.children.length>0)return"runnable, group";if(Q.runnable)return"runnable";return"group"}function pX(Q,X){let Z=[];return Z.push(...pQ(Q)),Z.push(...uQ(Q)),Z.push(...mQ(Q)),Z.push(...cQ(Q,X)),Z.join(`
4
- `)}function uX(Q,X){let Z=[],H=i(Q);if(Z.push(...pQ(Q)),Z.push(...uQ(Q)),Q.runnable)Z.push(...mQ(Q));return Z.push(...dX(Q,H)),Z.push(...cQ(Q,X)),Z.join(`
5
- `)}function mX(Q){let X=[...Q.path];for(let Z of Q.args)if(Z.variadic)X.push(Z.required?`<${Z.name}...>`:`[${Z.name}...]`);else X.push(Z.required?`<${Z.name}>`:`[${Z.name}]`);if(Q.flags.length>0)X.push("[options]");return X.join(" ")}function pQ(Q){let X=[`# \`${MQ(Q)}\``,""];if(Q.description)X.push(Q.description,"");return X}function uQ(Q){let X=Q.instructions??[];if(!VQ(X))return[];return["## Command Instructions","",...dQ(X),""]}function mQ(Q){let X=["## Usage","","```",Q.usage??mX(Q),"```",""];if(Q.args.length>0)X.push("## Arguments","",...cX(Q.args),"");if(Q.flags.length>0)X.push("## Flags","",...lX(Q.flags),"");return X.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.",""),X}function dX(Q,X){let Z=["## Subcommands",""];for(let H of Q.children){let $=i(H),W=wQ(X,$),Y=H.description?` - ${H.description}`:"";Z.push(`- [\`${H.name}\`](${W})${Y}`)}return Z.push(""),Z}function cX(Q){let X=[];X.push("| Argument | Type | Required | Description |"),X.push("| -------- | ---- | -------- | ----------- |");for(let Z of Q){let H=Z.variadic?`${Z.name}...`:Z.name,$=Z.required?"Yes":"No",W=hQ(rX(Z));X.push(`| \`${H}\` | ${Z.type} | ${$} | ${W} |`)}return X}function rX(Q){let X=[];if(Q.description)X.push(Q.description);if(Q.default!==void 0)X.push(`Default: \`${Q.default}\``);return X.join(". ")||"-"}function lX(Q){let X=[];X.push("| Flag | Type | Required | Description |"),X.push("| ---- | ---- | -------- | ----------- |");for(let Z of Q){let H=sX(Z),$=Z.required?"Yes":"No",W=hQ(iX(Z));X.push(`| ${H} | ${Z.type} | ${$} | ${W} |`)}return X}function sX(Q){let X=[`\`--${Q.name}\``];if(Q.short)X.push(`\`-${Q.short}\``);for(let Z of Q.aliases)X.push(`\`--${Z}\``);return X.join(", ")}function iX(Q){let X=[];if(Q.description)X.push(Q.description);if(Q.multiple)X.push("Can be specified multiple times");if(Q.default!==void 0)X.push(`Default: \`${Q.default}\``);return X.join(". ")||"-"}function dQ(Q){return Q.map((X)=>`- ${X}`)}function oX(Q){if(typeof Q==="string")return DQ(Q);return dQ(zQ(Q))}function cQ(Q,X){let Z=[],H=i(Q);if(Z.push("---"),Z.push(""),Q.path.length>1){let W=Q.path.slice(0,-1),Y=rQ(X,W);if(Y){let G=i(Y),x=wQ(H,G),q=MQ(Y);Z.push(`Parent: [\`${q}\`](${x})`),Z.push("")}}let $=wQ(H,"SKILL.md");return Z.push(`[Skill Overview](${$})`),Z.push(""),Z}function rQ(Q,X){if(aX(Q.path,X))return Q;for(let Z of Q.children){let H=rQ(Z,X);if(H)return H}return}function aX(Q,X){if(Q.length!==X.length)return!1;for(let Z=0;Z<Q.length;Z++)if(Q[Z]!==X[Z])return!1;return!0}import{readFile as nX}from"fs/promises";import{join as tX}from"path";var t="crust.json";async function BQ(Q){let X;try{X=await nX(tX(Q,t),"utf-8")}catch{return{status:"absent"}}let Z;try{Z=JSON.parse(X)}catch{return{status:"malformed",reason:"parse-error"}}if(typeof Z!=="object"||Z===null)return{status:"malformed",reason:"not-an-object"};let H=Z;if(typeof H.version!=="string")return{status:"malformed",reason:"missing-version"};let{version:$,kind:W}=H;if(W===void 0)return{status:"ok",manifest:{version:$,kind:"generated"}};if(W==="bundle"||W==="generated")return{status:"ok",manifest:{version:$,kind:W}};return{status:"malformed",reason:"unknown-kind",rawKind:typeof W==="string"?W:JSON.stringify(W)}}async function TQ(Q){let X=await BQ(Q);return X.status==="ok"?X.manifest:null}async function YQ(Q){return(await TQ(Q))?.version??null}var HZ="auto";async function $Z(Q){if(Q!==void 0)return Q;return[...S(),...await n()]}function XX(Q){if(Q!==void 0)return Q;return[...a]}var ZX=/^[a-z0-9]+(-[a-z0-9]+)*$/;function e(Q){return Q.length>=1&&Q.length<=64&&ZX.test(Q)}function QQ(Q){return Q}function jQ(Q){return Q.startsWith("use-")?Q:`use-${Q}`}async function XQ(Q){let{command:X,meta:Z,scope:H="global",clean:$=!0,force:W=!1,installMode:Y=HZ}=Q,G=await $Z(Q.agents),x=QQ(Z.name),q=jQ(Z.name);if(!e(x))throw Error(`Invalid skill name "${x}": must be 1\u201364 lowercase `+`alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens. Pattern: ${ZX.source}`);if(G.length===0)return{agents:[]};let F={...Z,name:x},K=SQ(X),V=fQ(K,F);return bQ({files:V,meta:F,agents:G,scope:H,clean:$,force:W,installMode:Y,kind:"generated",legacyResolvedName:q})}async function bQ(Q){let{files:X,meta:Z,agents:H,scope:$,clean:W,force:Y,installMode:G,kind:x,legacyResolvedName:q}=Q,F=H[0];if(!F)return{agents:[]};let K=BZ(Z,x),V=[...X,...K].sort((z,U)=>z.path<U.path?-1:z.path>U.path?1:0),w=V.map((z)=>z.path),M=new Map;for(let z of H){let U=N(z,$,Z.name),_=M.get(U);if(_)_.push(z);else M.set(U,[z])}let O=d($,Z.name),T=d($,q),b=new Map;for(let[z,U]of M){let _=U[0];if(!_)continue;b.set(z,await RQ({outputDir:z,legacyOutputDir:N(_,$,q),canonicalOutputDir:O,legacyCanonicalOutputDir:T}))}let v=await BQ(O),E=v.status==="ok"?v.manifest:null,P=E?.version??null;if((await EQ(O,O)).exists&&E===null&&!Y)throw new L({agent:F,outputDir:O,manifestMalformed:iQ(v)});if(E!==null&&E.kind!==x&&!Y)throw new L({agent:F,outputDir:O,kindMismatch:{existing:E.kind,attempted:x}});let D=E!==null&&E.kind!==x,g=P!==Z.version||D;if(g){if(W)await IQ(O);await HX(O,V)}let p=[];for(let[z,U]of M){let _=U[0];if(!_)continue;let B=b.get(z);if(!B)continue;if(B.current.inspection.exists&&!B.current.isCrustManaged&&!Y){let $Q=await BQ(z);throw new L({agent:_,outputDir:z,manifestMalformed:iQ($Q)})}if(B.current.manifest!==null&&B.current.manifest.kind!==x&&!Y)throw new L({agent:_,outputDir:z,kindMismatch:{existing:B.current.manifest.kind,attempted:x}});let j=await YZ({outputDir:z,canonicalOutputDir:O,allFiles:V,clean:W,installMode:G,inspection:B.current.inspection,installedVersion:B.preferredVersion,currentVersion:Z.version,installedKind:B.current.manifest?.kind??null,currentKind:x}),R=await JZ(B),C=WZ({installedVersion:B.preferredVersion,currentVersion:Z.version,canonicalChanged:g,pathChanged:j||R||B.preferredOutputDir!==z});for(let $Q of U)p.push({agent:$Q,outputDir:z,files:C==="up-to-date"?[]:w,status:C,previousVersion:C==="updated"?B.preferredVersion??void 0:void 0})}if(q!==Z.name){let z=await YQ(T);if(T!==O&&z!==null&&!await OQ(q,$))await qQ(T,{recursive:!0,force:!0})}return{agents:p}}async function KQ(Q){let{name:X,scope:Z="global"}=Q,H=XX(Q.agents),$=QQ(X),W=jQ(X),Y=d(Z,$),G=d(Z,W),x=[],q=new Map;for(let F of H){let K=N(F,Z,$),V=q.get(K);if(V)V.push(F);else q.set(K,[F])}for(let[F,K]of q){let V=K[0];if(!V)continue;let w=N(V,Z,W),M=await RQ({outputDir:F,legacyOutputDir:w,canonicalOutputDir:Y,legacyCanonicalOutputDir:G}),O=await _Q(M.current),T=M.legacy.outputDir!==M.current.outputDir?await _Q(M.legacy):!1,b=O||T,v=O?F:T?w:F;for(let E of K)x.push({agent:E,outputDir:v,status:b?"removed":"not-found"})}if(await YQ(Y)!==null&&!await OQ($,Z))await qQ(Y,{recursive:!0,force:!0});{let F=await YQ(G);if(G!==Y&&F!==null&&!await OQ(W,Z))await qQ(G,{recursive:!0,force:!0})}return{agents:x}}async function c(Q){let{name:X,scope:Z="global"}=Q,H=XX(Q.agents),$=QQ(X),W=jQ(X),Y=[],G=new Map;for(let x of H){let q=N(x,Z,$),F=G.get(q);if(F)F.push(x);else G.set(q,[x])}for(let[x,q]of G){let F=q[0];if(!F)continue;let K=N(F,Z,W),V=d(Z,$),w=d(Z,W),M=await RQ({outputDir:x,legacyOutputDir:K,canonicalOutputDir:V,legacyCanonicalOutputDir:w}),O=M.preferredOutputDir??x,T=M.preferredVersion;for(let b of q)Y.push({agent:b,outputDir:O,installed:T!==null,version:T??void 0})}return{agents:Y}}function iQ(Q){if(Q.status!=="malformed")return;return Q.rawKind!==void 0?{reason:Q.reason,rawKind:Q.rawKind}:{reason:Q.reason}}function WZ(Q){let{installedVersion:X,currentVersion:Z,canonicalChanged:H,pathChanged:$}=Q;if(X===null)return"installed";if(X===Z&&!H&&!$)return"up-to-date";return"updated"}async function YZ(Q){let{outputDir:X,canonicalOutputDir:Z,allFiles:H,clean:$,installMode:W,inspection:Y,installedVersion:G,currentVersion:x,installedKind:q,currentKind:F}=Q;if(W==="copy")return oQ({outputDir:X,allFiles:H,clean:$,inspection:Y,installedVersion:G,currentVersion:x,installedKind:q,currentKind:F});try{return await qZ({outputDir:X,canonicalOutputDir:Z,inspection:Y})}catch(K){if(W==="symlink")throw Error(`Failed to create symlink at "${X}" (installMode: symlink).`,{cause:K});let V=await EQ(X,Z);return oQ({outputDir:X,allFiles:H,clean:$,inspection:V,installedVersion:G,currentVersion:x,installedKind:q,currentKind:F})}}async function oQ(Q){let{outputDir:X,allFiles:Z,clean:H,inspection:$,installedVersion:W,currentVersion:Y,installedKind:G,currentKind:x}=Q,q=G!==null&&G!==x;if(!(!$.exists||$.isSymlink||W!==Y||q))return!1;if($.isSymlink||H)await IQ(X);return await HX(X,Z),!0}async function qZ(Q){let{outputDir:X,canonicalOutputDir:Z,inspection:H}=Q;if(H.exists&&H.isSymlink&&H.pointsToCanonical)return!1;if(H.exists)await IQ(X);return await zZ(Z,X),!0}async function EQ(Q,X){let Z;try{Z=await eX(Q)}catch{return{exists:!1,isSymlink:!1,pointsToCanonical:!1}}let H=process.platform==="win32"&&Z.isDirectory()&&await tQ(Q)!==null;if(!(Z.isSymbolicLink()||H))return{exists:!0,isSymlink:!1,pointsToCanonical:!1};let[W,Y,G]=await Promise.all([nQ(Q),nQ(X),tQ(Q)]);return{exists:!0,isSymlink:!0,pointsToCanonical:W!==null&&Y!==null&&W===Y||G===X}}async function aQ(Q,X){let[Z,H]=await Promise.all([TQ(Q),EQ(Q,X)]),$=Z?.version??null,W=$!==null||H.exists&&H.isSymlink&&H.pointsToCanonical;return{outputDir:Q,version:$,manifest:Z,inspection:H,isCrustManaged:W}}async function RQ(Q){let{outputDir:X,legacyOutputDir:Z,canonicalOutputDir:H,legacyCanonicalOutputDir:$}=Q,W=await aQ(X,H),Y=Z===X?W:await aQ(Z,$);if(W.isCrustManaged)return{current:W,legacy:Y,preferredVersion:W.version,preferredOutputDir:W.outputDir};if(Y.isCrustManaged)return{current:W,legacy:Y,preferredVersion:Y.version,preferredOutputDir:Y.outputDir};return{current:W,legacy:Y,preferredVersion:null,preferredOutputDir:null}}async function _Q(Q){if(!Q.isCrustManaged||!Q.inspection.exists)return!1;return await qQ(Q.outputDir,{recursive:!0,force:!0}),!0}async function JZ(Q){if(Q.legacy.outputDir===Q.current.outputDir)return!1;return _Q(Q.legacy)}async function nQ(Q){try{return await XZ(Q)}catch{return null}}async function tQ(Q){try{return await QZ(Q)}catch{return null}}async function zZ(Q,X){await eQ(QX(X),{recursive:!0});let Z=process.platform==="win32"?"junction":"dir";await ZZ(Q,X,Z)}async function OQ(Q,X){let Z=new Set;for(let H of a)Z.add(N(H,X,Q));for(let H of Z)if(await YQ(H)!==null)return!0;return!1}function BZ(Q,X){let Z={name:Q.name,description:Q.description,version:Q.version,kind:X};return[{path:t,content:`${JSON.stringify(Z,null,"\t")}
6
- `}]}async function IQ(Q){await qQ(Q,{recursive:!0,force:!0})}async function HX(Q,X){let Z=new Set;for(let $ of X){let W=sQ(Q,$.path),Y=QX(W);Z.add(Y)}let H=[...Z].sort();for(let $ of H)await eQ($,{recursive:!0});for(let $ of X){let W=sQ(Q,$.path);if(typeof $.content==="string")await lQ(W,$.content,"utf-8");else await lQ(W,$.content)}}var CQ="SKILL.md";function VZ(Q){let X=GQ(Q);if($X(X)&&!KZ(X).isDirectory())X=YX(X);while(!0){if($X(FQ(X,"package.json")))return X;let Z=YX(X);if(Z===X)return null;X=Z}}function UZ(Q){if(Q instanceof URL){if(Q.protocol!=="file:")throw Error(`Bundle URL must use file: protocol, got "${Q.protocol}".`);return xZ(Q)}if(FZ(Q))return GQ(Q);let X=process.argv[1];if(!X)throw Error(`Could not resolve relative bundle path "${Q}" because process.argv[1] is not set. Pass an absolute path or a file: URL sourceDir.`);let Z=VZ(GQ(X));if(!Z)throw Error(`Could not resolve relative bundle path "${Q}" from entrypoint "${X}" because no package.json was found in its parent directories. Pass an absolute path or a file: URL sourceDir.`);return GQ(Z,Q)}function wZ(Q){let X={name:null,description:null},H=(Q.startsWith("\uFEFF")?Q.slice(1):Q).split(/\r?\n/,51),$=0;while($<H.length&&H[$]?.trim()==="")$++;if($>=H.length||H[$]!=="---")return X;$++;let W=Math.min(50,H.length);for(let Y=$;Y<W;Y++){let G=H[Y];if(G===void 0)break;if(/^---\s*$/.test(G))break;let x=G.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*?)\s*$/);if(!x)continue;let q=x[1];if(q!=="name"&&q!=="description")continue;if(X[q]!==null)continue;X[q]=MZ(x[2]??"")}return X}function MZ(Q){let X=Q[0];if(X==='"'||X==="'"){let H=Q.indexOf(X,1);if(H!==-1)return Q.slice(1,H)}let Z=Q.search(/(^|\s)#/);return Z===-1?Q:Q.slice(0,Z).trimEnd()}function TZ(Q,X,Z){let H=X.endsWith(qX)?X:X+qX;if(Q!==X&&!Q.startsWith(H))throw Error(`Bundle path traversal rejected: "${Z}" resolves to "${Q}", which is outside the bundle root "${X}".`)}async function BX(Q,X,Z,H){let $=await GZ(Q,{withFileTypes:!0}),W=[];for(let Y of $){let G=Y.name,x=FQ(Q,G),q=Z===""?G:`${Z}/${G}`,F=await JX(x);TZ(F,X,x);let K=await zX(F);if(K.isDirectory()){if(H.has(F))continue;H.add(F),W.push(...await BX(x,X,q,H))}else if(K.isFile())W.push({relPath:q,absPath:x})}return W}async function _Z(Q){let X=UZ(Q),Z;try{Z=await JX(X)}catch(F){throw Error(`Bundle source directory "${X}" does not exist or is not accessible.`,{cause:F})}if(!(await zX(Z)).isDirectory())throw Error(`Bundle source path "${Z}" is not a directory.`);let W=await BX(Z,Z,"",new Set([Z])),Y=W.find((F)=>F.relPath===CQ);if(!Y)throw Error(`Bundle is missing SKILL.md at the bundle root "${Z}". Every skill bundle must contain a top-level SKILL.md file.`);if(W.some((F)=>F.relPath===t))throw Error(`Bundle source at "${Z}" contains a reserved file "${t}" at the root. Crust regenerates this file during installation; remove it from your bundle source.`);let G=await Promise.all(W.map(async(F)=>({path:F.relPath,content:await WX(F.absPath)}))),x=await WX(Y.absPath,"utf-8"),q=wZ(x);if(q.name===null||q.name==="")throw Error(`Bundle SKILL.md is missing a top-level \`name:\` field in its YAML frontmatter (at "${FQ(Z,CQ)}"). Add \`name: <skill-name>\` to the frontmatter block.`);if(q.description===null||q.description==="")throw Error(`Bundle SKILL.md is missing a top-level \`description:\` field in its YAML frontmatter (at "${FQ(Z,CQ)}"). Add \`description: <one-line summary>\` to the frontmatter block.`);return{files:G,frontmatter:{name:q.name,description:q.description}}}async function ZQ(Q){let{sourceDir:X,agents:Z,version:H,scope:$="global",clean:W=!0,force:Y=!1,installMode:G="auto",expectedName:x}=Q,{files:q,frontmatter:F}=await _Z(X),K=QQ(F.name);if(!e(K))throw Error(`Invalid skill name "${K}" in SKILL.md frontmatter: must be 1\u201364 lowercase `+"alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens.");if(x!==void 0&&K!==x)throw Error(`Bundle SKILL.md frontmatter name "${K}" does not match the expected name "${x}". Update the bundle's SKILL.md frontmatter \`name:\` field, or change the configured \`name\` to match.`);if(Z.length===0)return{agents:[]};let V={name:K,description:F.description,version:H};return bQ({files:[...q],meta:V,agents:Z,scope:$,clean:W,force:Y,installMode:G,kind:"bundle",legacyResolvedName:K})}import{Crust as GX,VALIDATION_MODE_ENV as OZ}from"@crustjs/core";import{spinner as y}from"@crustjs/progress";import{confirm as FX,multiselect as xX,select as jZ}from"@crustjs/prompts";import{bold as l,dim as k,yellow as o}from"@crustjs/style";var bZ="skill",VX="global",r="__universal__";function UX(Q){return Q==="global"||Q==="project"}function EZ(Q){return Q==="auto"||Q==="symlink"||Q==="copy"}function wX(Q){if(Q===void 0)return;if(!UX(Q))throw Error(`Invalid --scope value: ${String(Q)}. Expected "project" or "global".`);return Q}async function MX(Q,X){let Z=wX(Q);if(Z!==void 0)return Z;if(X.defaultScope)return X.defaultScope;return jZ({message:"Select scope",choices:[{label:"Project",value:"project"},{label:"Global",value:"global"}],default:VX})}function HQ(Q){let X=new Set(S()),Z=[];if(Q.some((H)=>X.has(H)))Z.push("Universal");for(let H of Q){if(X.has(H))continue;Z.push(A[H])}return Z}function xQ(Q){let X=new Set(S()),Z=[],H=Q.find(($)=>X.has($.agent));if(H)Z.push({label:"Universal",outputDir:H.outputDir});for(let $ of Q){if(X.has($.agent))continue;Z.push({label:A[$.agent],outputDir:$.outputDir})}return Z}function LQ(Q,X){return{name:Q.meta.name,description:Q.meta.description??"",version:X.version,instructions:X.instructions,license:X.license,allowedTools:X.allowedTools,compatibility:X.compatibility,disableModelInvocation:X.disableModelInvocation}}function RZ(Q,X){if(X===void 0)return[];if(!Array.isArray(X))throw Error(`skillPlugin: customSkills must be an array, got ${X===null?"null":typeof X}.`);if(X.length===0)return[];let Z=new Set;for(let H=0;H<X.length;H++){let $=X[H];if(!$||typeof $!=="object")throw Error(`skillPlugin: customSkills[${H}] must be an object, got ${$===null?"null":typeof $}.`);if(typeof $.name!=="string"||$.name.length===0)throw Error(`skillPlugin: customSkills[${H}].name must be a non-empty string.`);if(!e($.name))throw Error(`skillPlugin: customSkills[${H}].name "${$.name}" is not a valid skill name. `+"Must be 1\u201364 lowercase alphanumeric characters and hyphens, "+"no leading/trailing/consecutive hyphens.");if($.name===Q)throw Error(`skillPlugin: customSkills[${H}].name "${$.name}" collides with the main skill name. Custom skill bundle names must differ from the root command name.`);if(Z.has($.name))throw Error(`skillPlugin: customSkills contains duplicate name "${$.name}". Each entry must declare a unique name.`);if(Z.add($.name),$.version!==void 0&&(typeof $.version!=="string"||$.version.length===0))throw Error(`skillPlugin: customSkills[${H}].version (for "${$.name}") must be a non-empty string when set, or omitted to inherit the plugin's \`version\`.`);if(typeof $.sourceDir!=="string"&&!($.sourceDir instanceof URL))throw Error(`skillPlugin: customSkills[${H}].sourceDir (for "${$.name}") must be a string or URL, got ${typeof $.sourceDir}.`);if($.scope!==void 0&&!UX($.scope))throw Error(`skillPlugin: customSkills[${H}].scope (for "${$.name}") must be "project" or "global", got ${JSON.stringify($.scope)}.`);if($.installMode!==void 0&&!EZ($.installMode))throw Error(`skillPlugin: customSkills[${H}].installMode (for "${$.name}") must be "auto", "symlink", or "copy", got ${JSON.stringify($.installMode)}.`)}return X}function IZ(Q,X){let Z=Q.scope??X.defaultScope;if(Z!==void 0)return[u(Z)];return[...new Set(["project","global"].map((H)=>u(H)))]}async function CZ(Q,X){let Z=[...S(),...m()];if(Z.length===0)return;let H=IZ(Q,X),$=Q.installMode??X.installMode,W=Q.version??X.version;for(let Y of H){let x=(await c({name:Q.name,agents:Z,scope:Y})).agents.filter((q)=>{if(!q.installed)return!1;let F=N(q.agent,Y,Q.name);return q.version!==W||q.outputDir!==F});if(x.length===0)continue;try{await y({message:`Updating ${Y} skills [${Q.name}]...`,task:async({updateMessage:q})=>{let F=await ZQ({sourceDir:Q.sourceDir,agents:x.map((w)=>w.agent),version:W,scope:Y,installMode:$,expectedName:Q.name}),K=F.agents.filter((w)=>w.status==="updated").map((w)=>w.agent),V=HQ(K);if(V.length>0)q(`Updated bundle "${Q.name}" to v${W} for ${V.join(", ")} (${Y})`);return F}})}catch(q){if(q instanceof L){let F=q.details.kindMismatch?` (existing skill is "${q.details.kindMismatch.existing}", attempted "${q.details.kindMismatch.attempted}")`:"";console.warn(o(`Skill conflict [${Q.name}]: "${q.details.outputDir}" already exists but conflicts with the requested install${F}. Skipping auto-update for ${Y}. Delete or rename the conflicting skill to resolve.`))}else throw q}}}function NQ(Q,X,Z,H){if(!H.installed)return!1;let $=N(Q,X,Z.name);return H.version!==Z.version||H.outputDir!==$}async function LZ(Q,X,Z){let H=[...S(),...m()];if(H.length===0){await KX(Z,X);return}let $=LQ(Q,X),W=[...new Set(["project","global"].map((Y)=>u(Y)))];for(let Y of W){let x=(await c({name:$.name,agents:H,scope:Y})).agents.filter((q)=>NQ(q.agent,Y,$,q));if(x.length===0)continue;try{await y({message:`Updating ${Y} skills...`,task:async({updateMessage:q})=>{let F=await XQ({command:Q,meta:$,agents:x.map((w)=>w.agent),scope:Y,installMode:X.installMode}),K=F.agents.filter((w)=>w.status==="updated").map((w)=>w.agent),V=HQ(K);if(V.length>0)q(`Updated skill "${$.name}" to v${$.version} for ${V.join(", ")} (${Y})`);return F}})}catch(q){if(q instanceof L)console.warn(o(`Skill conflict: "${q.details.outputDir}" already exists but was not created by ${$.name}. Skipping auto-update for ${Y}. Delete or rename the conflicting skill to resolve.`));else throw q}}await KX(Z,X)}async function KX(Q,X){for(let Z of Q)try{await CZ(Z,X)}catch(H){let $=H instanceof Error?H.message:String(H);console.warn(o(`Skill auto-update failed [${Z.name}]: ${$}. Continuing with remaining skills.`))}}function NZ(Q){let X;return{name:"skills",async setup(Z,H){X=Z.rootCommand;let $=Q.command??bZ,W=RZ(X.meta.name,Q.customSkills);if(H.addSubCommand(X,$,kZ(X,Q,W,$)),process.env[OZ]==="1")return;if(Z.argv[0]===$)return;if(Q.autoUpdate!==!1)await LZ(X,Q,W)}}}async function PZ(Q){let{entry:X,options:Z,scope:H,installAll:$,isInteractive:W}=Q,Y=X.installMode??Z.installMode,G=X.version??Z.version,x=await n(),q=S(),F=m(),K=await c({name:X.name,agents:[...q,...F],scope:H}),V=new Set(K.agents.filter((z)=>z.installed).map((z)=>z.agent)),w=new Set(x),M=new Map(K.agents.map((z)=>[z.agent,z])),O=F.filter((z)=>{if(w.has(z))return!0;return M.get(z)?.installed===!0}),T=O.filter((z)=>V.has(z)),b=[];if(q.length>0){let z=q[0];if(!z)throw Error("Expected at least one universal agent");let _=M.get(z)?.outputDir??"path unavailable";b.push({label:"Universal",value:r,hint:_});let B=q.map((j)=>A[j]).join(", ");if(W&&!$)console.log(k(`Agents supporting universal skills: ${B}`))}for(let z of O){let _=M.get(z)?.outputDir??"path unavailable";b.push({label:A[z],value:z,hint:_})}let v=q.length>0&&q.every((z)=>V.has(z)),E=[...T.filter((z)=>!q.includes(z))];if(v)E.unshift(r);let P;if($)P=[...q,...O];else{let z=b.length===0?[]:await xX({message:`Select agents to install skills for [${X.name}]`,choices:b,default:E,required:!1}),U=new Set(z.filter((_)=>_!==r));if(z.includes(r))for(let _ of q)U.add(_);P=[...U]}let I=P.filter((z)=>!V.has(z)),D=P.filter((z)=>{let U=M.get(z);if(!U?.installed)return!1;let _=N(z,H,X.name);return U.version!==G||U.outputDir!==_}),g=[...V].filter((z)=>!P.includes(z)),p=[...I,...D];if(p.length>0)try{let z=await y({message:`Installing skills [${X.name}]...`,task:async()=>ZQ({sourceDir:X.sourceDir,agents:p,version:G,scope:H,installMode:Y,expectedName:X.name})});console.log(`
7
- ${l(`Installed bundle "${X.name}" v${G}`)}`);for(let U of xQ(z.agents))console.log(k(` ${U.label} \u2192 ${U.outputDir}`))}catch(z){if(z instanceof L){let U=z.details.kindMismatch?` (existing is a "${z.details.kindMismatch.existing}" skill, attempted "${z.details.kindMismatch.attempted}")`:" but was not created by Crust";if($?!0:await FX({message:`"${z.details.outputDir}" already exists${U}. Overwrite?`,default:!1})){let B=await y({message:`Overwriting bundle [${X.name}]...`,task:async()=>ZQ({sourceDir:X.sourceDir,agents:[z.details.agent],version:G,scope:H,force:!0,installMode:Y,expectedName:X.name})});console.log(`
8
- ${l(`Installed bundle "${X.name}" v${G}`)}`);for(let j of xQ(B.agents))console.log(k(` ${j.label} \u2192 ${j.outputDir}`))}else console.log(k(`
9
- Skipped ${A[z.details.agent]} [${X.name}]`))}else throw z}if(g.length>0){let U=(await y({message:`Removing skills [${X.name}]...`,task:async()=>KQ({name:X.name,agents:g,scope:H})})).agents.filter((B)=>B.status==="removed").map((B)=>B.agent),_=HQ(U);if(_.length>0)console.log(`
10
- ${l(`Removed bundle "${X.name}" from ${_.join(", ")}`)}`)}if(p.length===0&&g.length===0)console.log(k(`No changes [${X.name}].`))}function kZ(Q,X,Z,H){let $=DZ(Q,X,Z);return new GX(H).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 Y=LQ(Q,X),G=W.flags.all===!0,x=!!process.stdin.isTTY,q=G?wX(W.flags.scope)??X.defaultScope??VX:await MX(W.flags.scope,X),F=await n(),K=S(),V=m(),w=await c({name:Y.name,agents:[...K,...V],scope:q}),M=new Set(w.agents.filter((B)=>B.installed).map((B)=>B.agent)),O=new Set(F),T=new Map(w.agents.map((B)=>[B.agent,B])),b=V.filter((B)=>{if(O.has(B))return!0;return T.get(B)?.installed===!0}),v=b.filter((B)=>M.has(B)),E=[];if(K.length>0){let B=K[0];if(!B)throw Error("Expected at least one universal agent");let R=T.get(B)?.outputDir??"path unavailable";E.push({label:"Universal",value:r,hint:R});let C=K.map(($Q)=>A[$Q]).join(", ");if(x&&!G)console.log(k(`Agents supporting universal skills: ${C}`))}for(let B of b){let R=T.get(B)?.outputDir??"path unavailable";E.push({label:A[B],value:B,hint:R})}let P=K.length>0&&K.every((B)=>M.has(B)),I=[...v.filter((B)=>!K.includes(B))];if(P)I.unshift(r);let D;if(G)D=[...K,...b];else{let B=E.length===0?[]:await xX({message:"Select agents to install skills for",choices:E,default:I,required:!1}),j=new Set(B.filter((R)=>R!==r));if(B.includes(r))for(let R of K)j.add(R);D=[...j]}let g=D.filter((B)=>!M.has(B)),p=D.filter((B)=>{let j=T.get(B);return j!==void 0&&NQ(B,q,Y,j)}),z=[...M].filter((B)=>!D.includes(B)),U=[...g,...p];if(U.length>0)try{let B=await y({message:"Installing skills...",task:async()=>XQ({command:Q,meta:Y,agents:U,scope:q,installMode:X.installMode})});console.log(`
11
- ${l(`Installed "${Y.name}" v${Y.version}`)}`);for(let j of xQ(B.agents))console.log(k(` ${j.label} \u2192 ${j.outputDir}`))}catch(B){if(B instanceof L)if(G?!0:await FX({message:`"${B.details.outputDir}" already exists but was not created by Crust. Overwrite?`,default:!1})){let R=await y({message:"Overwriting skill...",task:async()=>XQ({command:Q,meta:Y,agents:[B.details.agent],scope:q,force:!0,installMode:X.installMode})});console.log(`
12
- ${l(`Installed "${Y.name}" v${Y.version}`)}`);for(let C of xQ(R.agents))console.log(k(` ${C.label} \u2192 ${C.outputDir}`))}else console.log(k(`
13
- Skipped ${A[B.details.agent]}`));else throw B}if(z.length>0){let j=(await y({message:"Removing skills...",task:async()=>KQ({name:Y.name,agents:z,scope:q})})).agents.filter((C)=>C.status==="removed").map((C)=>C.agent),R=HQ(j);if(R.length>0)console.log(`
14
- ${l(`Removed from ${R.join(", ")}`)}`)}if(U.length===0&&z.length===0)console.log(k("No changes."));let _=[];for(let B of Z){let j=B.scope??q;try{await PZ({entry:B,options:X,scope:j,installAll:G,isInteractive:x})}catch(R){let C=R instanceof Error?R.message:String(R);console.warn(o(`Skill reconciliation failed [${B.name}]: ${C}. Continuing with remaining skills.`)),_.push(B.name)}}if(_.length>0)process.exitCode=1}).command($)._node}function DZ(Q,X,Z){return new GX("update").meta({description:"Update installed skills to latest version"}).flags({scope:{type:"string",description:"Update scope (project or global)"}}).run(async(H)=>{let $=await MX(H.flags.scope,X),W=u($),Y=[...S(),...m()],G=LQ(Q,X),q=(await c({name:G.name,agents:Y,scope:$})).agents.filter((K)=>NQ(K.agent,$,G,K));if(q.length===0)console.log(k(`No updates needed (${W}).`));else try{let V=(await y({message:`Updating ${W} skills...`,task:async()=>XQ({command:Q,meta:G,agents:q.map((M)=>M.agent),scope:$,installMode:X.installMode})})).agents.filter((M)=>M.status==="updated").map((M)=>M.agent),w=HQ(V);if(w.length>0)console.log(`
15
- ${l(`Updated "${G.name}" to v${G.version} for ${w.join(", ")} (${W})`)}`)}catch(K){if(K instanceof L)console.warn(o(`Skipped ${A[K.details.agent]}: "${K.details.outputDir}" already exists but was not created by ${G.name}. Delete or rename the conflicting directory to resolve.`));else throw K}let F=[];for(let K of Z){let V=K.scope??$,w=u(V),M=K.installMode??X.installMode,O=K.version??X.version;try{let b=(await c({name:K.name,agents:Y,scope:V})).agents.filter((I)=>{if(!I.installed)return!1;let D=N(I.agent,V,K.name);return I.version!==O||I.outputDir!==D});if(b.length===0){console.log(k(`No updates needed [${K.name}] (${w}).`));continue}let E=(await y({message:`Updating ${w} skills [${K.name}]...`,task:async()=>ZQ({sourceDir:K.sourceDir,agents:b.map((I)=>I.agent),version:O,scope:V,installMode:M,expectedName:K.name})})).agents.filter((I)=>I.status==="updated").map((I)=>I.agent),P=HQ(E);if(P.length>0)console.log(`
16
- ${l(`Updated bundle "${K.name}" to v${O} for ${P.join(", ")} (${w})`)}`)}catch(T){if(T instanceof L){let b=T.details.kindMismatch?` (existing is a "${T.details.kindMismatch.existing}" skill, attempted "${T.details.kindMismatch.attempted}")`:"";console.warn(o(`Skipped ${A[T.details.agent]} [${K.name}]: "${T.details.outputDir}" already exists${b}. Delete or rename the conflicting directory to resolve.`))}else{let b=T instanceof Error?T.message:String(T);console.warn(o(`Skill update failed [${K.name}]: ${b}. Continuing with remaining skills.`)),F.push(K.name)}}}if(F.length>0)process.exitCode=1})}export{KQ as uninstallSkill,c as skillStatus,NZ as skillPlugin,QQ as resolveSkillName,d as resolveCanonicalSkillPath,e as isValidSkillName,RX as isUniversalAgent,ZQ as installSkillBundle,S as getUniversalAgents,m as getAdditionalAgents,XQ as generateSkill,n as detectInstalledAgents,NX as annotate,L as SkillConflictError};
2
+ import{accessSync as wX,constants as MX,statSync as TX}from"fs";import{homedir as JQ}from"os";import{delimiter as _X,join as J}from"path";var y=J(".agents","skills"),OX=J(".crust","skills");function NQ(Q){if(Q!==JQ())return J(Q,".config");let X=process.env.XDG_CONFIG_HOME?.trim();return X&&X.length>0?X:J(Q,".config")}function f(Q){return J(Q,".agents","skills")}function jX(Q){return J(Q,".crust","skills")}function u(Q){return Q==="project"&&process.cwd()===JQ()?"global":Q}var s={amp:{label:"Amp",class:"universal",projectSkillsDir:y,globalSkillsDir:f},adal:{label:"AdaL",class:"additional",projectSkillsDir:J(".adal","skills"),globalSkillsDir:(Q)=>J(Q,".adal","skills"),detectCommands:["adal"]},antigravity:{label:"Antigravity",class:"additional",projectSkillsDir:J(".agent","skills"),globalSkillsDir:(Q)=>J(Q,".gemini","antigravity","skills"),detectCommands:["antigravity"]},augment:{label:"Augment",class:"additional",projectSkillsDir:J(".augment","skills"),globalSkillsDir:(Q)=>J(Q,".augment","skills"),detectCommands:["augment"]},"claude-code":{label:"Claude Code",class:"additional",projectSkillsDir:J(".claude","skills"),globalSkillsDir:(Q)=>J(process.env.CLAUDE_CONFIG_DIR?.trim()||J(Q,".claude"),"skills"),detectCommands:["claude","claude-code"]},cline:{label:"Cline",class:"universal",projectSkillsDir:y,globalSkillsDir:f},codebuddy:{label:"CodeBuddy",class:"additional",projectSkillsDir:J(".codebuddy","skills"),globalSkillsDir:(Q)=>J(Q,".codebuddy","skills"),detectCommands:["codebuddy"]},codex:{label:"Codex",class:"universal",projectSkillsDir:y,globalSkillsDir:f},"command-code":{label:"Command Code",class:"additional",projectSkillsDir:J(".commandcode","skills"),globalSkillsDir:(Q)=>J(Q,".commandcode","skills"),detectCommands:["command-code","commandcode"]},continue:{label:"Continue",class:"additional",projectSkillsDir:J(".continue","skills"),globalSkillsDir:(Q)=>J(Q,".continue","skills"),detectCommands:["continue"]},cortex:{label:"Cortex Code",class:"additional",projectSkillsDir:J(".cortex","skills"),globalSkillsDir:(Q)=>J(Q,".snowflake","cortex","skills"),detectCommands:["cortex"]},crush:{label:"Crush",class:"additional",projectSkillsDir:J(".crush","skills"),globalSkillsDir:(Q)=>J(NQ(Q),"crush","skills"),detectCommands:["crush"]},cursor:{label:"Cursor",class:"universal",projectSkillsDir:y,globalSkillsDir:f},droid:{label:"Droid",class:"additional",projectSkillsDir:J(".factory","skills"),globalSkillsDir:(Q)=>J(Q,".factory","skills"),detectCommands:["droid"]},"gemini-cli":{label:"Gemini CLI",class:"universal",projectSkillsDir:y,globalSkillsDir:f},"github-copilot":{label:"GitHub Copilot",class:"universal",projectSkillsDir:y,globalSkillsDir:f},goose:{label:"Goose",class:"additional",projectSkillsDir:J(".goose","skills"),globalSkillsDir:(Q)=>J(NQ(Q),"goose","skills"),detectCommands:["goose"]},"iflow-cli":{label:"iFlow CLI",class:"additional",projectSkillsDir:J(".iflow","skills"),globalSkillsDir:(Q)=>J(Q,".iflow","skills"),detectCommands:["iflow","iflow-cli"]},junie:{label:"Junie",class:"additional",projectSkillsDir:J(".junie","skills"),globalSkillsDir:(Q)=>J(Q,".junie","skills"),detectCommands:["junie"]},kilo:{label:"Kilo Code",class:"additional",projectSkillsDir:J(".kilocode","skills"),globalSkillsDir:(Q)=>J(Q,".kilocode","skills"),detectCommands:["kilo","kilocode"]},"kimi-cli":{label:"Kimi Code CLI",class:"universal",projectSkillsDir:y,globalSkillsDir:f},"kiro-cli":{label:"Kiro CLI",class:"additional",projectSkillsDir:J(".kiro","skills"),globalSkillsDir:(Q)=>J(Q,".kiro","skills"),detectCommands:["kiro","kiro-cli"]},kode:{label:"Kode",class:"additional",projectSkillsDir:J(".kode","skills"),globalSkillsDir:(Q)=>J(Q,".kode","skills"),detectCommands:["kode"]},mcpjam:{label:"MCPJam",class:"additional",projectSkillsDir:J(".mcpjam","skills"),globalSkillsDir:(Q)=>J(Q,".mcpjam","skills"),detectCommands:["mcpjam"]},"mistral-vibe":{label:"Mistral Vibe",class:"additional",projectSkillsDir:J(".vibe","skills"),globalSkillsDir:(Q)=>J(Q,".vibe","skills"),detectCommands:["mistral-vibe","vibe"]},mux:{label:"Mux",class:"additional",projectSkillsDir:J(".mux","skills"),globalSkillsDir:(Q)=>J(Q,".mux","skills"),detectCommands:["mux"]},neovate:{label:"Neovate",class:"additional",projectSkillsDir:J(".neovate","skills"),globalSkillsDir:(Q)=>J(Q,".neovate","skills"),detectCommands:["neovate"]},opencode:{label:"OpenCode",class:"universal",projectSkillsDir:y,globalSkillsDir:f},openclaw:{label:"OpenClaw",class:"additional",projectSkillsDir:"skills",globalSkillsDir:(Q)=>J(Q,".openclaw","skills"),detectCommands:["openclaw"]},openhands:{label:"OpenHands",class:"additional",projectSkillsDir:J(".openhands","skills"),globalSkillsDir:(Q)=>J(Q,".openhands","skills"),detectCommands:["openhands"]},pi:{label:"Pi",class:"additional",projectSkillsDir:J(".pi","skills"),globalSkillsDir:(Q)=>J(Q,".pi","agent","skills"),detectCommands:["pi"]},pochi:{label:"Pochi",class:"additional",projectSkillsDir:J(".pochi","skills"),globalSkillsDir:(Q)=>J(Q,".pochi","skills"),detectCommands:["pochi"]},qoder:{label:"Qoder",class:"additional",projectSkillsDir:J(".qoder","skills"),globalSkillsDir:(Q)=>J(Q,".qoder","skills"),detectCommands:["qoder"]},"qwen-code":{label:"Qwen Code",class:"additional",projectSkillsDir:J(".qwen","skills"),globalSkillsDir:(Q)=>J(Q,".qwen","skills"),detectCommands:["qwen","qwen-code"]},replit:{label:"Replit",class:"universal",projectSkillsDir:y,globalSkillsDir:f},roo:{label:"Roo Code",class:"additional",projectSkillsDir:J(".roo","skills"),globalSkillsDir:(Q)=>J(Q,".roo","skills"),detectCommands:["roo","roo-code"]},trae:{label:"Trae",class:"additional",projectSkillsDir:J(".trae","skills"),globalSkillsDir:(Q)=>J(Q,".trae","skills"),detectCommands:["trae"]},"trae-cn":{label:"Trae CN",class:"additional",projectSkillsDir:J(".trae","skills"),globalSkillsDir:(Q)=>J(Q,".trae-cn","skills"),detectCommands:["trae-cn","trae"]},windsurf:{label:"Windsurf",class:"additional",projectSkillsDir:J(".windsurf","skills"),globalSkillsDir:(Q)=>J(Q,".codeium","windsurf","skills"),detectCommands:["windsurf"]},zencoder:{label:"Zencoder",class:"additional",projectSkillsDir:J(".zencoder","skills"),globalSkillsDir:(Q)=>J(Q,".zencoder","skills"),detectCommands:["zencoder"]}},a=Object.keys(s),A=Object.fromEntries(a.map((Q)=>[Q,s[Q].label]));function S(){return a.filter((Q)=>s[Q].class==="universal")}function m(){return a.filter((Q)=>s[Q].class==="additional")}function bX(Q){return s[Q].class==="universal"}async function n(Q){let X=typeof Q==="string"?{home:Q}:Q??{},Z=X.cwd??process.cwd(),H=X.commandChecker??((W)=>Promise.resolve(RX(W))),$=[];for(let W of m()){let Y=s[W].detectCommands??[],G=!1;for(let x of Y)if(await H(x,Z)){G=!0;break}if(G)$.push(W)}return $}function N(Q,X,Z){let H=u(X),$=s[Q];if(H==="project")return J(process.cwd(),$.projectSkillsDir,Z);return J($.globalSkillsDir(JQ()),Z)}function c(Q,X){if(u(Q)==="project")return J(process.cwd(),OX,X);return J(jX(JQ()),X)}function RX(Q){let Z=(process.env.PATH??"").split(_X).filter((W)=>W.length>0),H=process.platform==="win32",$=H?(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";").filter((W)=>W.length>0):[];for(let W of Z){if(!H&&PQ(J(W,Q)))return!0;if(H){for(let Y of $)if(PQ(J(W,Q+Y)))return!0}}return!1}function PQ(Q){try{if(!TX(Q).isFile())return!1;return wX(Q,MX.X_OK),!0}catch{return!1}}import{Crust as EX}from"@crustjs/core";function BQ(Q){if(Q===void 0)return[];return(Array.isArray(Q)?Q:[Q]).flatMap((Z)=>Z.split(/\r?\n/)).map((Z)=>Z.trim()).filter((Z)=>Z.length>0)}function kQ(Q){let X=Q?.trim();if(!X)return[];return X.split(/\r?\n/)}function FQ(Q){return Q.length>0}var DQ=Symbol("crust.skill.commandAnnotations");function IX(Q){return Q instanceof EX?Q._node:Q}function CX(Q,X){let Z=IX(Q),H=BQ(typeof X==="string"||Array.isArray(X)?X:X.instructions??[]);if(H.length===0)return Q;let $=xQ(Z)?.instructions??[],W=[...new Set([...$,...H])];return Object.defineProperty(Z,DQ,{value:{instructions:W},enumerable:!0,configurable:!0}),Q}function xQ(Q){let X=Q[DQ];if(!X?.instructions||X.instructions.length===0)return;return{instructions:[...X.instructions]}}import{readdir as BZ,readFile as $X,realpath as YX,stat as qX}from"fs/promises";import{join as IQ,sep as WX}from"path";import{resolveSourceDir as zZ}from"@crustjs/utils";import{lstat as nX,mkdir as eQ,readlink as tX,realpath as eX,rm as qQ,symlink as QZ,writeFile as lQ}from"fs/promises";import{dirname as QX,join as sQ}from"path";class L extends Error{name="SkillConflictError";details;constructor(Q){super(LX(Q));this.details=Q}}function LX(Q){let X=`Skill conflict for agent "${Q.agent}": directory "${Q.outputDir}"`;if(Q.kindMismatch)return`${X} was installed as a "${Q.kindMismatch.existing}" skill but "${Q.kindMismatch.attempted}" was attempted. Use force: true to overwrite, or uninstall the existing skill first.`;if(Q.manifestMalformed){let{reason:Z,rawKind:H}=Q.manifestMalformed;switch(Z){case"unknown-kind":return`${X} was created by Crust but its crust.json declares an unrecognized kind "${H??"<unknown>"}" \u2014 likely a hand-edit typo or a `+"crust.json written by a newer Crust release. Fix the kind field, upgrade Crust, or pass force: true to overwrite.";case"parse-error":return`${X} contains a crust.json that is not valid JSON. Repair the file, or pass force: true to overwrite the directory.`;case"not-an-object":return`${X} contains a crust.json whose top-level value is not a JSON object. Repair the file, or pass force: true to overwrite the directory.`;case"missing-version":return`${X} contains a crust.json with no "version" string. Repair the file, or pass force: true to overwrite the directory.`}}return`${X} already exists but was not created by Crust (no crust.json found). Delete or rename the conflicting skill to resolve.`}function AQ(Q){return SQ(Q,[])}function SQ(Q,X){let Z=NX(Q.meta.name),H=[...X,Z],$=PX(Q.args),W=DX(Q.effectiveFlags),Y=SX(Q.subCommands,H),G=xQ(Q);return{name:Z,path:H,description:Q.meta.description,usage:Q.meta.usage,instructions:G?.instructions,runnable:typeof Q.run==="function",args:$,flags:W,children:Y}}function NX(Q){return Q.trim().toLowerCase()}function PX(Q){if(!Q||Q.length===0)return[];return Q.map(kX)}function kX(Q){let X={name:Q.name,type:vQ(Q.type),required:Q.required===!0,variadic:Q.variadic===!0};if(Q.description!==void 0)X.description=Q.description;if(Q.default!==void 0)X.default=hQ(Q.default);return X}function DX(Q){if(!Q)return[];return Object.keys(Q).sort().map((Z)=>{return AX(Z,Q[Z])})}function AX(Q,X){let Z={name:Q,type:vQ(X.type),required:X.required===!0,multiple:X.multiple===!0,short:X.short,aliases:X.aliases?[...X.aliases].sort():[]};if(X.description!==void 0)Z.description=X.description;if(X.default!==void 0)Z.default=hQ(X.default);return Z}function SX(Q,X){return Object.keys(Q).sort().map((H)=>{return SQ(Q[H],X)})}function vQ(Q){if(Q==="number"||Q==="boolean")return Q;return"string"}function hQ(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function WQ(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 yQ(Q){return Q.replace(/(?<!\\)\|/g,"\\|")}function fQ(Q,X){let Z=[],H=gQ(Q);Z.push({path:"SKILL.md",content:vX(Q,X,H)});for(let $ of H){let W=i($),Y=$.children.length>0?gX($,Q):fX($,Q);Z.push({path:W,content:Y})}return Z}function gQ(Q){let X=[Q];for(let Z of Q.children)X.push(...gQ(Z));return X}function i(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function UQ(Q){return Q.path.join(" ")}function VQ(Q,X){let Z=Q.split("/").slice(0,-1),H=X.split("/"),$=0;while($<Z.length&&$<H.length&&Z[$]===H[$])$++;let W=Z.length-$,Y=H.slice($);if(W===0)return Y.join("/");return[...Array.from({length:W},()=>".."),...Y].join("/")}function vX(Q,X,Z){let H=[];if(H.push("---"),H.push(`name: ${WQ(X.name)}`),H.push(`description: ${WQ(X.description)}`),X.license)H.push(`license: ${WQ(X.license)}`);if(X.compatibility)H.push(`compatibility: ${WQ(X.compatibility)}`);if(X.disableModelInvocation)H.push("disable-model-invocation: true");if(X.allowedTools)H.push(`allowed-tools: ${WQ(X.allowedTools)}`);if(H.push("metadata:"),H.push(` version: "${X.version}"`),H.push("---"),H.push(""),H.push(`# ${X.name}`),H.push(""),Q.description)H.push(Q.description),H.push("");H.push(`You should use this skill when you need accurate help with \`${X.name}\` commands, including command selection, syntax, arguments, flags, defaults, and subcommands.`),H.push("");let $=sX(X.instructions);if(H.push("## How to Use This Skill"),H.push(""),H.push("1. You must find the command that best matches the user's task from the Command Reference below."),H.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."),H.push("3. You should read only the linked file or files you need from `commands/`."),H.push("4. You must read a command's file before answering a command-specific question or suggesting that command."),H.push("5. You must treat the command file as the source of truth for usage, arguments, flags, aliases, and defaults."),H.push("6. If a flag, argument, alias, or default is not documented there, you must say it is not documented instead of guessing."),H.push(""),FQ($))H.push("## General Guidance"),H.push(""),H.push(...$),H.push("");if(H.push("## Command Reference"),H.push(""),H.push("You should use this table to locate the command file you need."),H.push(""),H.push(...hX(Z)),H.push(""),Q.runnable){H.push("## Usage"),H.push("");let W=i(Q);H.push(`The root command is directly executable. You should see [${Q.name}](${W}) for usage details.`),H.push("")}return H.join(`
3
+ `)}function hX(Q){let X=[];X.push("| Command | Type | Documentation |"),X.push("| ------- | ---- | ------------- |");for(let Z of Q){let H=UQ(Z),$=i(Z),W=yX(Z);X.push(`| \`${H}\` | ${W} | [${$}](${$}) |`)}return X}function yX(Q){if(Q.runnable&&Q.children.length>0)return"runnable, group";if(Q.runnable)return"runnable";return"group"}function fX(Q,X){let Z=[];return Z.push(...pQ(Q)),Z.push(...uQ(Q)),Z.push(...mQ(Q)),Z.push(...dQ(Q,X)),Z.join(`
4
+ `)}function gX(Q,X){let Z=[],H=i(Q);if(Z.push(...pQ(Q)),Z.push(...uQ(Q)),Q.runnable)Z.push(...mQ(Q));return Z.push(...uX(Q,H)),Z.push(...dQ(Q,X)),Z.join(`
5
+ `)}function pX(Q){let X=[...Q.path];for(let Z of Q.args)if(Z.variadic)X.push(Z.required?`<${Z.name}...>`:`[${Z.name}...]`);else X.push(Z.required?`<${Z.name}>`:`[${Z.name}]`);if(Q.flags.length>0)X.push("[options]");return X.join(" ")}function pQ(Q){let X=[`# \`${UQ(Q)}\``,""];if(Q.description)X.push(Q.description,"");return X}function uQ(Q){let X=Q.instructions??[];if(!FQ(X))return[];return["## Command Instructions","",...cQ(X),""]}function mQ(Q){let X=["## Usage","","```",Q.usage??pX(Q),"```",""];if(Q.args.length>0)X.push("## Arguments","",...mX(Q.args),"");if(Q.flags.length>0)X.push("## Flags","",...dX(Q.flags),"");return X.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.",""),X}function uX(Q,X){let Z=["## Subcommands",""];for(let H of Q.children){let $=i(H),W=VQ(X,$),Y=H.description?` - ${H.description}`:"";Z.push(`- [\`${H.name}\`](${W})${Y}`)}return Z.push(""),Z}function mX(Q){let X=[];X.push("| Argument | Type | Required | Description |"),X.push("| -------- | ---- | -------- | ----------- |");for(let Z of Q){let H=Z.variadic?`${Z.name}...`:Z.name,$=Z.required?"Yes":"No",W=yQ(cX(Z));X.push(`| \`${H}\` | ${Z.type} | ${$} | ${W} |`)}return X}function cX(Q){let X=[];if(Q.description)X.push(Q.description);if(Q.default!==void 0)X.push(`Default: \`${Q.default}\``);return X.join(". ")||"-"}function dX(Q){let X=[];X.push("| Flag | Type | Required | Description |"),X.push("| ---- | ---- | -------- | ----------- |");for(let Z of Q){let H=rX(Z),$=Z.required?"Yes":"No",W=yQ(lX(Z));X.push(`| ${H} | ${Z.type} | ${$} | ${W} |`)}return X}function rX(Q){let X=[`\`--${Q.name}\``];if(Q.short)X.push(`\`-${Q.short}\``);for(let Z of Q.aliases)X.push(`\`--${Z}\``);return X.join(", ")}function lX(Q){let X=[];if(Q.description)X.push(Q.description);if(Q.multiple)X.push("Can be specified multiple times");if(Q.default!==void 0)X.push(`Default: \`${Q.default}\``);return X.join(". ")||"-"}function cQ(Q){return Q.map((X)=>`- ${X}`)}function sX(Q){if(typeof Q==="string")return kQ(Q);return cQ(BQ(Q))}function dQ(Q,X){let Z=[],H=i(Q);if(Z.push("---"),Z.push(""),Q.path.length>1){let W=Q.path.slice(0,-1),Y=rQ(X,W);if(Y){let G=i(Y),x=VQ(H,G),q=UQ(Y);Z.push(`Parent: [\`${q}\`](${x})`),Z.push("")}}let $=VQ(H,"SKILL.md");return Z.push(`[Skill Overview](${$})`),Z.push(""),Z}function rQ(Q,X){if(iX(Q.path,X))return Q;for(let Z of Q.children){let H=rQ(Z,X);if(H)return H}return}function iX(Q,X){if(Q.length!==X.length)return!1;for(let Z=0;Z<Q.length;Z++)if(Q[Z]!==X[Z])return!1;return!0}import{readFile as oX}from"fs/promises";import{join as aX}from"path";var t="crust.json";async function zQ(Q){let X;try{X=await oX(aX(Q,t),"utf-8")}catch{return{status:"absent"}}let Z;try{Z=JSON.parse(X)}catch{return{status:"malformed",reason:"parse-error"}}if(typeof Z!=="object"||Z===null)return{status:"malformed",reason:"not-an-object"};let H=Z;if(typeof H.version!=="string")return{status:"malformed",reason:"missing-version"};let{version:$,kind:W}=H;if(W===void 0)return{status:"ok",manifest:{version:$,kind:"generated"}};if(W==="bundle"||W==="generated")return{status:"ok",manifest:{version:$,kind:W}};return{status:"malformed",reason:"unknown-kind",rawKind:typeof W==="string"?W:JSON.stringify(W)}}async function wQ(Q){let X=await zQ(Q);return X.status==="ok"?X.manifest:null}async function YQ(Q){return(await wQ(Q))?.version??null}var XZ="auto";async function ZZ(Q){if(Q!==void 0)return Q;return[...S(),...await n()]}function XX(Q){if(Q!==void 0)return Q;return[...a]}var ZX=/^[a-z0-9]+(-[a-z0-9]+)*$/;function e(Q){return Q.length>=1&&Q.length<=64&&ZX.test(Q)}function QQ(Q){return Q}function _Q(Q){return Q.startsWith("use-")?Q:`use-${Q}`}async function XQ(Q){let{command:X,meta:Z,scope:H="global",clean:$=!0,force:W=!1,installMode:Y=XZ}=Q,G=await ZZ(Q.agents),x=QQ(Z.name),q=_Q(Z.name);if(!e(x))throw Error(`Invalid skill name "${x}": must be 1\u201364 lowercase `+`alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens. Pattern: ${ZX.source}`);if(G.length===0)return{agents:[]};let F={...Z,name:x},K=AQ(X),V=fQ(K,F);return OQ({files:V,meta:F,agents:G,scope:H,clean:$,force:W,installMode:Y,kind:"generated",legacyResolvedName:q})}async function OQ(Q){let{files:X,meta:Z,agents:H,scope:$,clean:W,force:Y,installMode:G,kind:x,legacyResolvedName:q}=Q,F=H[0];if(!F)return{agents:[]};let K=JZ(Z,x),V=[...X,...K].sort((B,U)=>B.path<U.path?-1:B.path>U.path?1:0),w=V.map((B)=>B.path),M=new Map;for(let B of H){let U=N(B,$,Z.name),_=M.get(U);if(_)_.push(B);else M.set(U,[B])}let O=c($,Z.name),T=c($,q),b=new Map;for(let[B,U]of M){let _=U[0];if(!_)continue;b.set(B,await bQ({outputDir:B,legacyOutputDir:N(_,$,q),canonicalOutputDir:O,legacyCanonicalOutputDir:T}))}let v=await zQ(O),R=v.status==="ok"?v.manifest:null,P=R?.version??null;if((await jQ(O,O)).exists&&R===null&&!Y)throw new L({agent:F,outputDir:O,manifestMalformed:iQ(v)});if(R!==null&&R.kind!==x&&!Y)throw new L({agent:F,outputDir:O,kindMismatch:{existing:R.kind,attempted:x}});let D=R!==null&&R.kind!==x,g=P!==Z.version||D;if(g){if(W)await RQ(O);await HX(O,V)}let p=[];for(let[B,U]of M){let _=U[0];if(!_)continue;let z=b.get(B);if(!z)continue;if(z.current.inspection.exists&&!z.current.isCrustManaged&&!Y){let $Q=await zQ(B);throw new L({agent:_,outputDir:B,manifestMalformed:iQ($Q)})}if(z.current.manifest!==null&&z.current.manifest.kind!==x&&!Y)throw new L({agent:_,outputDir:B,kindMismatch:{existing:z.current.manifest.kind,attempted:x}});let j=await $Z({outputDir:B,canonicalOutputDir:O,allFiles:V,clean:W,installMode:G,inspection:z.current.inspection,installedVersion:z.preferredVersion,currentVersion:Z.version,installedKind:z.current.manifest?.kind??null,currentKind:x}),E=await YZ(z),C=HZ({installedVersion:z.preferredVersion,currentVersion:Z.version,canonicalChanged:g,pathChanged:j||E||z.preferredOutputDir!==B});for(let $Q of U)p.push({agent:$Q,outputDir:B,files:C==="up-to-date"?[]:w,status:C,previousVersion:C==="updated"?z.preferredVersion??void 0:void 0})}if(q!==Z.name){let B=await YQ(T);if(T!==O&&B!==null&&!await TQ(q,$))await qQ(T,{recursive:!0,force:!0})}return{agents:p}}async function KQ(Q){let{name:X,scope:Z="global"}=Q,H=XX(Q.agents),$=QQ(X),W=_Q(X),Y=c(Z,$),G=c(Z,W),x=[],q=new Map;for(let F of H){let K=N(F,Z,$),V=q.get(K);if(V)V.push(F);else q.set(K,[F])}for(let[F,K]of q){let V=K[0];if(!V)continue;let w=N(V,Z,W),M=await bQ({outputDir:F,legacyOutputDir:w,canonicalOutputDir:Y,legacyCanonicalOutputDir:G}),O=await MQ(M.current),T=M.legacy.outputDir!==M.current.outputDir?await MQ(M.legacy):!1,b=O||T,v=O?F:T?w:F;for(let R of K)x.push({agent:R,outputDir:v,status:b?"removed":"not-found"})}if(await YQ(Y)!==null&&!await TQ($,Z))await qQ(Y,{recursive:!0,force:!0});{let F=await YQ(G);if(G!==Y&&F!==null&&!await TQ(W,Z))await qQ(G,{recursive:!0,force:!0})}return{agents:x}}async function d(Q){let{name:X,scope:Z="global"}=Q,H=XX(Q.agents),$=QQ(X),W=_Q(X),Y=[],G=new Map;for(let x of H){let q=N(x,Z,$),F=G.get(q);if(F)F.push(x);else G.set(q,[x])}for(let[x,q]of G){let F=q[0];if(!F)continue;let K=N(F,Z,W),V=c(Z,$),w=c(Z,W),M=await bQ({outputDir:x,legacyOutputDir:K,canonicalOutputDir:V,legacyCanonicalOutputDir:w}),O=M.preferredOutputDir??x,T=M.preferredVersion;for(let b of q)Y.push({agent:b,outputDir:O,installed:T!==null,version:T??void 0})}return{agents:Y}}function iQ(Q){if(Q.status!=="malformed")return;return Q.rawKind!==void 0?{reason:Q.reason,rawKind:Q.rawKind}:{reason:Q.reason}}function HZ(Q){let{installedVersion:X,currentVersion:Z,canonicalChanged:H,pathChanged:$}=Q;if(X===null)return"installed";if(X===Z&&!H&&!$)return"up-to-date";return"updated"}async function $Z(Q){let{outputDir:X,canonicalOutputDir:Z,allFiles:H,clean:$,installMode:W,inspection:Y,installedVersion:G,currentVersion:x,installedKind:q,currentKind:F}=Q;if(W==="copy")return oQ({outputDir:X,allFiles:H,clean:$,inspection:Y,installedVersion:G,currentVersion:x,installedKind:q,currentKind:F});try{return await WZ({outputDir:X,canonicalOutputDir:Z,inspection:Y})}catch(K){if(W==="symlink")throw Error(`Failed to create symlink at "${X}" (installMode: symlink).`,{cause:K});let V=await jQ(X,Z);return oQ({outputDir:X,allFiles:H,clean:$,inspection:V,installedVersion:G,currentVersion:x,installedKind:q,currentKind:F})}}async function oQ(Q){let{outputDir:X,allFiles:Z,clean:H,inspection:$,installedVersion:W,currentVersion:Y,installedKind:G,currentKind:x}=Q,q=G!==null&&G!==x;if(!(!$.exists||$.isSymlink||W!==Y||q))return!1;if($.isSymlink||H)await RQ(X);return await HX(X,Z),!0}async function WZ(Q){let{outputDir:X,canonicalOutputDir:Z,inspection:H}=Q;if(H.exists&&H.isSymlink&&H.pointsToCanonical)return!1;if(H.exists)await RQ(X);return await qZ(Z,X),!0}async function jQ(Q,X){let Z;try{Z=await nX(Q)}catch{return{exists:!1,isSymlink:!1,pointsToCanonical:!1}}let H=process.platform==="win32"&&Z.isDirectory()&&await tQ(Q)!==null;if(!(Z.isSymbolicLink()||H))return{exists:!0,isSymlink:!1,pointsToCanonical:!1};let[W,Y,G]=await Promise.all([nQ(Q),nQ(X),tQ(Q)]);return{exists:!0,isSymlink:!0,pointsToCanonical:W!==null&&Y!==null&&W===Y||G===X}}async function aQ(Q,X){let[Z,H]=await Promise.all([wQ(Q),jQ(Q,X)]),$=Z?.version??null,W=$!==null||H.exists&&H.isSymlink&&H.pointsToCanonical;return{outputDir:Q,version:$,manifest:Z,inspection:H,isCrustManaged:W}}async function bQ(Q){let{outputDir:X,legacyOutputDir:Z,canonicalOutputDir:H,legacyCanonicalOutputDir:$}=Q,W=await aQ(X,H),Y=Z===X?W:await aQ(Z,$);if(W.isCrustManaged)return{current:W,legacy:Y,preferredVersion:W.version,preferredOutputDir:W.outputDir};if(Y.isCrustManaged)return{current:W,legacy:Y,preferredVersion:Y.version,preferredOutputDir:Y.outputDir};return{current:W,legacy:Y,preferredVersion:null,preferredOutputDir:null}}async function MQ(Q){if(!Q.isCrustManaged||!Q.inspection.exists)return!1;return await qQ(Q.outputDir,{recursive:!0,force:!0}),!0}async function YZ(Q){if(Q.legacy.outputDir===Q.current.outputDir)return!1;return MQ(Q.legacy)}async function nQ(Q){try{return await eX(Q)}catch{return null}}async function tQ(Q){try{return await tX(Q)}catch{return null}}async function qZ(Q,X){await eQ(QX(X),{recursive:!0});let Z=process.platform==="win32"?"junction":"dir";await QZ(Q,X,Z)}async function TQ(Q,X){let Z=new Set;for(let H of a)Z.add(N(H,X,Q));for(let H of Z)if(await YQ(H)!==null)return!0;return!1}function JZ(Q,X){let Z={name:Q.name,description:Q.description,version:Q.version,kind:X};return[{path:t,content:`${JSON.stringify(Z,null,"\t")}
6
+ `}]}async function RQ(Q){await qQ(Q,{recursive:!0,force:!0})}async function HX(Q,X){let Z=new Set;for(let $ of X){let W=sQ(Q,$.path),Y=QX(W);Z.add(Y)}let H=[...Z].sort();for(let $ of H)await eQ($,{recursive:!0});for(let $ of X){let W=sQ(Q,$.path);if(typeof $.content==="string")await lQ(W,$.content,"utf-8");else await lQ(W,$.content)}}var EQ="SKILL.md";function KZ(Q){let X={name:null,description:null},H=(Q.startsWith("\uFEFF")?Q.slice(1):Q).split(/\r?\n/,51),$=0;while($<H.length&&H[$]?.trim()==="")$++;if($>=H.length||H[$]!=="---")return X;$++;let W=Math.min(50,H.length);for(let Y=$;Y<W;Y++){let G=H[Y];if(G===void 0)break;if(/^---\s*$/.test(G))break;let x=G.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*?)\s*$/);if(!x)continue;let q=x[1];if(q!=="name"&&q!=="description")continue;if(X[q]!==null)continue;X[q]=GZ(x[2]??"")}return X}function GZ(Q){let X=Q[0];if(X==='"'||X==="'"){let H=Q.indexOf(X,1);if(H!==-1)return Q.slice(1,H)}let Z=Q.search(/(^|\s)#/);return Z===-1?Q:Q.slice(0,Z).trimEnd()}function FZ(Q,X,Z){let H=X.endsWith(WX)?X:X+WX;if(Q!==X&&!Q.startsWith(H))throw Error(`Bundle path traversal rejected: "${Z}" resolves to "${Q}", which is outside the bundle root "${X}".`)}async function JX(Q,X,Z,H){let $=await BZ(Q,{withFileTypes:!0}),W=[];for(let Y of $){let G=Y.name,x=IQ(Q,G),q=Z===""?G:`${Z}/${G}`,F=await YX(x);FZ(F,X,x);let K=await qX(F);if(K.isDirectory()){if(H.has(F))continue;H.add(F),W.push(...await JX(x,X,q,H))}else if(K.isFile())W.push({relPath:q,absPath:x})}return W}async function xZ(Q){let X=zZ(Q),Z;try{Z=await YX(X)}catch(F){throw Error(`Bundle source directory "${X}" does not exist or is not accessible.`,{cause:F})}if(!(await qX(Z)).isDirectory())throw Error(`Bundle source path "${Z}" is not a directory.`);let W=await JX(Z,Z,"",new Set([Z])),Y=W.find((F)=>F.relPath===EQ);if(!Y)throw Error(`Bundle is missing SKILL.md at the bundle root "${Z}". Every skill bundle must contain a top-level SKILL.md file.`);if(W.some((F)=>F.relPath===t))throw Error(`Bundle source at "${Z}" contains a reserved file "${t}" at the root. Crust regenerates this file during installation; remove it from your bundle source.`);let G=await Promise.all(W.map(async(F)=>({path:F.relPath,content:await $X(F.absPath)}))),x=await $X(Y.absPath,"utf-8"),q=KZ(x);if(q.name===null||q.name==="")throw Error(`Bundle SKILL.md is missing a top-level \`name:\` field in its YAML frontmatter (at "${IQ(Z,EQ)}"). Add \`name: <skill-name>\` to the frontmatter block.`);if(q.description===null||q.description==="")throw Error(`Bundle SKILL.md is missing a top-level \`description:\` field in its YAML frontmatter (at "${IQ(Z,EQ)}"). Add \`description: <one-line summary>\` to the frontmatter block.`);return{files:G,frontmatter:{name:q.name,description:q.description}}}async function ZQ(Q){let{sourceDir:X,agents:Z,version:H,scope:$="global",clean:W=!0,force:Y=!1,installMode:G="auto",expectedName:x}=Q,{files:q,frontmatter:F}=await xZ(X),K=QQ(F.name);if(!e(K))throw Error(`Invalid skill name "${K}" in SKILL.md frontmatter: must be 1\u201364 lowercase `+"alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens.");if(x!==void 0&&K!==x)throw Error(`Bundle SKILL.md frontmatter name "${K}" does not match the expected name "${x}". Update the bundle's SKILL.md frontmatter \`name:\` field, or change the configured \`name\` to match.`);if(Z.length===0)return{agents:[]};let V={name:K,description:F.description,version:H};return OQ({files:[...q],meta:V,agents:Z,scope:$,clean:W,force:Y,installMode:G,kind:"bundle",legacyResolvedName:K})}import{Crust as zX,VALIDATION_MODE_ENV as VZ}from"@crustjs/core";import{spinner as h}from"@crustjs/progress";import{confirm as KX,multiselect as GX,select as UZ}from"@crustjs/prompts";import{bold as l,dim as k,yellow as o}from"@crustjs/style";var wZ="skill",FX="global",r="__universal__";function xX(Q){return Q==="global"||Q==="project"}function MZ(Q){return Q==="auto"||Q==="symlink"||Q==="copy"}function VX(Q){if(Q===void 0)return;if(!xX(Q))throw Error(`Invalid --scope value: ${String(Q)}. Expected "project" or "global".`);return Q}async function UX(Q,X){let Z=VX(Q);if(Z!==void 0)return Z;if(X.defaultScope)return X.defaultScope;return UZ({message:"Select scope",choices:[{label:"Project",value:"project"},{label:"Global",value:"global"}],default:FX})}function HQ(Q){let X=new Set(S()),Z=[];if(Q.some((H)=>X.has(H)))Z.push("Universal");for(let H of Q){if(X.has(H))continue;Z.push(A[H])}return Z}function GQ(Q){let X=new Set(S()),Z=[],H=Q.find(($)=>X.has($.agent));if(H)Z.push({label:"Universal",outputDir:H.outputDir});for(let $ of Q){if(X.has($.agent))continue;Z.push({label:A[$.agent],outputDir:$.outputDir})}return Z}function CQ(Q,X){return{name:Q.meta.name,description:Q.meta.description??"",version:X.version,instructions:X.instructions,license:X.license,allowedTools:X.allowedTools,compatibility:X.compatibility,disableModelInvocation:X.disableModelInvocation}}function TZ(Q,X){if(X===void 0)return[];if(!Array.isArray(X))throw Error(`skillPlugin: customSkills must be an array, got ${X===null?"null":typeof X}.`);if(X.length===0)return[];let Z=new Set;for(let H=0;H<X.length;H++){let $=X[H];if(!$||typeof $!=="object")throw Error(`skillPlugin: customSkills[${H}] must be an object, got ${$===null?"null":typeof $}.`);if(typeof $.name!=="string"||$.name.length===0)throw Error(`skillPlugin: customSkills[${H}].name must be a non-empty string.`);if(!e($.name))throw Error(`skillPlugin: customSkills[${H}].name "${$.name}" is not a valid skill name. `+"Must be 1\u201364 lowercase alphanumeric characters and hyphens, "+"no leading/trailing/consecutive hyphens.");if($.name===Q)throw Error(`skillPlugin: customSkills[${H}].name "${$.name}" collides with the main skill name. Custom skill bundle names must differ from the root command name.`);if(Z.has($.name))throw Error(`skillPlugin: customSkills contains duplicate name "${$.name}". Each entry must declare a unique name.`);if(Z.add($.name),$.version!==void 0&&(typeof $.version!=="string"||$.version.length===0))throw Error(`skillPlugin: customSkills[${H}].version (for "${$.name}") must be a non-empty string when set, or omitted to inherit the plugin's \`version\`.`);if(typeof $.sourceDir!=="string"&&!($.sourceDir instanceof URL))throw Error(`skillPlugin: customSkills[${H}].sourceDir (for "${$.name}") must be a string or URL, got ${typeof $.sourceDir}.`);if($.scope!==void 0&&!xX($.scope))throw Error(`skillPlugin: customSkills[${H}].scope (for "${$.name}") must be "project" or "global", got ${JSON.stringify($.scope)}.`);if($.installMode!==void 0&&!MZ($.installMode))throw Error(`skillPlugin: customSkills[${H}].installMode (for "${$.name}") must be "auto", "symlink", or "copy", got ${JSON.stringify($.installMode)}.`)}return X}function _Z(Q,X){let Z=Q.scope??X.defaultScope;if(Z!==void 0)return[u(Z)];return[...new Set(["project","global"].map((H)=>u(H)))]}async function OZ(Q,X){let Z=[...S(),...m()];if(Z.length===0)return;let H=_Z(Q,X),$=Q.installMode??X.installMode,W=Q.version??X.version;for(let Y of H){let x=(await d({name:Q.name,agents:Z,scope:Y})).agents.filter((q)=>{if(!q.installed)return!1;let F=N(q.agent,Y,Q.name);return q.version!==W||q.outputDir!==F});if(x.length===0)continue;try{await h({message:`Updating ${Y} skills [${Q.name}]...`,task:async({updateMessage:q})=>{let F=await ZQ({sourceDir:Q.sourceDir,agents:x.map((w)=>w.agent),version:W,scope:Y,installMode:$,expectedName:Q.name}),K=F.agents.filter((w)=>w.status==="updated").map((w)=>w.agent),V=HQ(K);if(V.length>0)q(`Updated bundle "${Q.name}" to v${W} for ${V.join(", ")} (${Y})`);return F}})}catch(q){if(q instanceof L){let F=q.details.kindMismatch?` (existing skill is "${q.details.kindMismatch.existing}", attempted "${q.details.kindMismatch.attempted}")`:"";console.warn(o(`Skill conflict [${Q.name}]: "${q.details.outputDir}" already exists but conflicts with the requested install${F}. Skipping auto-update for ${Y}. Delete or rename the conflicting skill to resolve.`))}else throw q}}}function LQ(Q,X,Z,H){if(!H.installed)return!1;let $=N(Q,X,Z.name);return H.version!==Z.version||H.outputDir!==$}async function jZ(Q,X,Z){let H=[...S(),...m()];if(H.length===0){await BX(Z,X);return}let $=CQ(Q,X),W=[...new Set(["project","global"].map((Y)=>u(Y)))];for(let Y of W){let x=(await d({name:$.name,agents:H,scope:Y})).agents.filter((q)=>LQ(q.agent,Y,$,q));if(x.length===0)continue;try{await h({message:`Updating ${Y} skills...`,task:async({updateMessage:q})=>{let F=await XQ({command:Q,meta:$,agents:x.map((w)=>w.agent),scope:Y,installMode:X.installMode}),K=F.agents.filter((w)=>w.status==="updated").map((w)=>w.agent),V=HQ(K);if(V.length>0)q(`Updated skill "${$.name}" to v${$.version} for ${V.join(", ")} (${Y})`);return F}})}catch(q){if(q instanceof L)console.warn(o(`Skill conflict: "${q.details.outputDir}" already exists but was not created by ${$.name}. Skipping auto-update for ${Y}. Delete or rename the conflicting skill to resolve.`));else throw q}}await BX(Z,X)}async function BX(Q,X){for(let Z of Q)try{await OZ(Z,X)}catch(H){let $=H instanceof Error?H.message:String(H);console.warn(o(`Skill auto-update failed [${Z.name}]: ${$}. Continuing with remaining skills.`))}}function bZ(Q){let X;return{name:"skills",async setup(Z,H){X=Z.rootCommand;let $=Q.command??wZ,W=TZ(X.meta.name,Q.customSkills);if(H.addSubCommand(X,$,EZ(X,Q,W,$)),process.env[VZ]==="1")return;if(Z.argv[0]===$)return;if(Q.autoUpdate!==!1)await jZ(X,Q,W)}}}async function RZ(Q){let{entry:X,options:Z,scope:H,installAll:$,isInteractive:W}=Q,Y=X.installMode??Z.installMode,G=X.version??Z.version,x=await n(),q=S(),F=m(),K=await d({name:X.name,agents:[...q,...F],scope:H}),V=new Set(K.agents.filter((B)=>B.installed).map((B)=>B.agent)),w=new Set(x),M=new Map(K.agents.map((B)=>[B.agent,B])),O=F.filter((B)=>{if(w.has(B))return!0;return M.get(B)?.installed===!0}),T=O.filter((B)=>V.has(B)),b=[];if(q.length>0){let B=q[0];if(!B)throw Error("Expected at least one universal agent");let _=M.get(B)?.outputDir??"path unavailable";b.push({label:"Universal",value:r,hint:_});let z=q.map((j)=>A[j]).join(", ");if(W&&!$)console.log(k(`Agents supporting universal skills: ${z}`))}for(let B of O){let _=M.get(B)?.outputDir??"path unavailable";b.push({label:A[B],value:B,hint:_})}let v=q.length>0&&q.every((B)=>V.has(B)),R=[...T.filter((B)=>!q.includes(B))];if(v)R.unshift(r);let P;if($)P=[...q,...O];else{let B=b.length===0?[]:await GX({message:`Select agents to install skills for [${X.name}]`,choices:b,default:R,required:!1}),U=new Set(B.filter((_)=>_!==r));if(B.includes(r))for(let _ of q)U.add(_);P=[...U]}let I=P.filter((B)=>!V.has(B)),D=P.filter((B)=>{let U=M.get(B);if(!U?.installed)return!1;let _=N(B,H,X.name);return U.version!==G||U.outputDir!==_}),g=[...V].filter((B)=>!P.includes(B)),p=[...I,...D];if(p.length>0)try{let B=await h({message:`Installing skills [${X.name}]...`,task:async()=>ZQ({sourceDir:X.sourceDir,agents:p,version:G,scope:H,installMode:Y,expectedName:X.name})});console.log(`
7
+ ${l(`Installed bundle "${X.name}" v${G}`)}`);for(let U of GQ(B.agents))console.log(k(` ${U.label} \u2192 ${U.outputDir}`))}catch(B){if(B instanceof L){let U=B.details.kindMismatch?` (existing is a "${B.details.kindMismatch.existing}" skill, attempted "${B.details.kindMismatch.attempted}")`:" but was not created by Crust";if($?!0:await KX({message:`"${B.details.outputDir}" already exists${U}. Overwrite?`,default:!1})){let z=await h({message:`Overwriting bundle [${X.name}]...`,task:async()=>ZQ({sourceDir:X.sourceDir,agents:[B.details.agent],version:G,scope:H,force:!0,installMode:Y,expectedName:X.name})});console.log(`
8
+ ${l(`Installed bundle "${X.name}" v${G}`)}`);for(let j of GQ(z.agents))console.log(k(` ${j.label} \u2192 ${j.outputDir}`))}else console.log(k(`
9
+ Skipped ${A[B.details.agent]} [${X.name}]`))}else throw B}if(g.length>0){let U=(await h({message:`Removing skills [${X.name}]...`,task:async()=>KQ({name:X.name,agents:g,scope:H})})).agents.filter((z)=>z.status==="removed").map((z)=>z.agent),_=HQ(U);if(_.length>0)console.log(`
10
+ ${l(`Removed bundle "${X.name}" from ${_.join(", ")}`)}`)}if(p.length===0&&g.length===0)console.log(k(`No changes [${X.name}].`))}function EZ(Q,X,Z,H){let $=IZ(Q,X,Z);return new zX(H).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 Y=CQ(Q,X),G=W.flags.all===!0,x=!!process.stdin.isTTY,q=G?VX(W.flags.scope)??X.defaultScope??FX:await UX(W.flags.scope,X),F=await n(),K=S(),V=m(),w=await d({name:Y.name,agents:[...K,...V],scope:q}),M=new Set(w.agents.filter((z)=>z.installed).map((z)=>z.agent)),O=new Set(F),T=new Map(w.agents.map((z)=>[z.agent,z])),b=V.filter((z)=>{if(O.has(z))return!0;return T.get(z)?.installed===!0}),v=b.filter((z)=>M.has(z)),R=[];if(K.length>0){let z=K[0];if(!z)throw Error("Expected at least one universal agent");let E=T.get(z)?.outputDir??"path unavailable";R.push({label:"Universal",value:r,hint:E});let C=K.map(($Q)=>A[$Q]).join(", ");if(x&&!G)console.log(k(`Agents supporting universal skills: ${C}`))}for(let z of b){let E=T.get(z)?.outputDir??"path unavailable";R.push({label:A[z],value:z,hint:E})}let P=K.length>0&&K.every((z)=>M.has(z)),I=[...v.filter((z)=>!K.includes(z))];if(P)I.unshift(r);let D;if(G)D=[...K,...b];else{let z=R.length===0?[]:await GX({message:"Select agents to install skills for",choices:R,default:I,required:!1}),j=new Set(z.filter((E)=>E!==r));if(z.includes(r))for(let E of K)j.add(E);D=[...j]}let g=D.filter((z)=>!M.has(z)),p=D.filter((z)=>{let j=T.get(z);return j!==void 0&&LQ(z,q,Y,j)}),B=[...M].filter((z)=>!D.includes(z)),U=[...g,...p];if(U.length>0)try{let z=await h({message:"Installing skills...",task:async()=>XQ({command:Q,meta:Y,agents:U,scope:q,installMode:X.installMode})});console.log(`
11
+ ${l(`Installed "${Y.name}" v${Y.version}`)}`);for(let j of GQ(z.agents))console.log(k(` ${j.label} \u2192 ${j.outputDir}`))}catch(z){if(z instanceof L)if(G?!0:await KX({message:`"${z.details.outputDir}" already exists but was not created by Crust. Overwrite?`,default:!1})){let E=await h({message:"Overwriting skill...",task:async()=>XQ({command:Q,meta:Y,agents:[z.details.agent],scope:q,force:!0,installMode:X.installMode})});console.log(`
12
+ ${l(`Installed "${Y.name}" v${Y.version}`)}`);for(let C of GQ(E.agents))console.log(k(` ${C.label} \u2192 ${C.outputDir}`))}else console.log(k(`
13
+ Skipped ${A[z.details.agent]}`));else throw z}if(B.length>0){let j=(await h({message:"Removing skills...",task:async()=>KQ({name:Y.name,agents:B,scope:q})})).agents.filter((C)=>C.status==="removed").map((C)=>C.agent),E=HQ(j);if(E.length>0)console.log(`
14
+ ${l(`Removed from ${E.join(", ")}`)}`)}if(U.length===0&&B.length===0)console.log(k("No changes."));let _=[];for(let z of Z){let j=z.scope??q;try{await RZ({entry:z,options:X,scope:j,installAll:G,isInteractive:x})}catch(E){let C=E instanceof Error?E.message:String(E);console.warn(o(`Skill reconciliation failed [${z.name}]: ${C}. Continuing with remaining skills.`)),_.push(z.name)}}if(_.length>0)process.exitCode=1}).command($)._node}function IZ(Q,X,Z){return new zX("update").meta({description:"Update installed skills to latest version"}).flags({scope:{type:"string",description:"Update scope (project or global)"}}).run(async(H)=>{let $=await UX(H.flags.scope,X),W=u($),Y=[...S(),...m()],G=CQ(Q,X),q=(await d({name:G.name,agents:Y,scope:$})).agents.filter((K)=>LQ(K.agent,$,G,K));if(q.length===0)console.log(k(`No updates needed (${W}).`));else try{let V=(await h({message:`Updating ${W} skills...`,task:async()=>XQ({command:Q,meta:G,agents:q.map((M)=>M.agent),scope:$,installMode:X.installMode})})).agents.filter((M)=>M.status==="updated").map((M)=>M.agent),w=HQ(V);if(w.length>0)console.log(`
15
+ ${l(`Updated "${G.name}" to v${G.version} for ${w.join(", ")} (${W})`)}`)}catch(K){if(K instanceof L)console.warn(o(`Skipped ${A[K.details.agent]}: "${K.details.outputDir}" already exists but was not created by ${G.name}. Delete or rename the conflicting directory to resolve.`));else throw K}let F=[];for(let K of Z){let V=K.scope??$,w=u(V),M=K.installMode??X.installMode,O=K.version??X.version;try{let b=(await d({name:K.name,agents:Y,scope:V})).agents.filter((I)=>{if(!I.installed)return!1;let D=N(I.agent,V,K.name);return I.version!==O||I.outputDir!==D});if(b.length===0){console.log(k(`No updates needed [${K.name}] (${w}).`));continue}let R=(await h({message:`Updating ${w} skills [${K.name}]...`,task:async()=>ZQ({sourceDir:K.sourceDir,agents:b.map((I)=>I.agent),version:O,scope:V,installMode:M,expectedName:K.name})})).agents.filter((I)=>I.status==="updated").map((I)=>I.agent),P=HQ(R);if(P.length>0)console.log(`
16
+ ${l(`Updated bundle "${K.name}" to v${O} for ${P.join(", ")} (${w})`)}`)}catch(T){if(T instanceof L){let b=T.details.kindMismatch?` (existing is a "${T.details.kindMismatch.existing}" skill, attempted "${T.details.kindMismatch.attempted}")`:"";console.warn(o(`Skipped ${A[T.details.agent]} [${K.name}]: "${T.details.outputDir}" already exists${b}. Delete or rename the conflicting directory to resolve.`))}else{let b=T instanceof Error?T.message:String(T);console.warn(o(`Skill update failed [${K.name}]: ${b}. Continuing with remaining skills.`)),F.push(K.name)}}}if(F.length>0)process.exitCode=1})}export{KQ as uninstallSkill,d as skillStatus,bZ as skillPlugin,QQ as resolveSkillName,c as resolveCanonicalSkillPath,e as isValidSkillName,bX as isUniversalAgent,ZQ as installSkillBundle,S as getUniversalAgents,m as getAdditionalAgents,XQ as generateSkill,n as detectInstalledAgents,CX as annotate,L as SkillConflictError};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crustjs/skills",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Agent skill generation from Crust command definitions",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,15 +45,16 @@
45
45
  "dependencies": {
46
46
  "@crustjs/progress": "0.0.4",
47
47
  "@crustjs/prompts": "0.1.0",
48
- "@crustjs/style": "0.2.0"
48
+ "@crustjs/style": "0.2.0",
49
+ "@crustjs/utils": "0.0.2"
49
50
  },
50
51
  "devDependencies": {
51
52
  "@crustjs/config": "0.0.0",
52
- "@crustjs/core": "0.0.17",
53
+ "@crustjs/core": "0.0.18",
53
54
  "bunup": "^0.16.31"
54
55
  },
55
56
  "peerDependencies": {
56
- "@crustjs/core": "0.0.17",
57
+ "@crustjs/core": "0.0.18",
57
58
  "typescript": "^6.0.3"
58
59
  }
59
60
  }