@crustjs/skills 0.0.11 → 0.0.12
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 +11 -5
- package/dist/index.d.ts +18 -33
- package/dist/index.js +9 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,8 +108,8 @@ If auto-update does not appear to work:
|
|
|
108
108
|
|
|
109
109
|
- Ensure plugin is passed to `runMain(..., { plugins: [...] })`.
|
|
110
110
|
- Ensure at least one supported agent is detected for your scope:
|
|
111
|
-
- `scope: "global"` -> `~/.claude
|
|
112
|
-
- `scope: "project"` -> `<cwd>/.claude
|
|
111
|
+
- `scope: "global"` -> checks supported global agent config roots (for example `~/.claude`, `~/.config/opencode`, `~/.codex`)
|
|
112
|
+
- `scope: "project"` -> checks project roots first (for example `<cwd>/.claude`, `<cwd>/.opencode`) then falls back to global roots
|
|
113
113
|
- Check for existing conflicting skill directories without `crust.json`.
|
|
114
114
|
|
|
115
115
|
## Recommended Export Pattern
|
|
@@ -297,7 +297,7 @@ skills/use-my-cli/
|
|
|
297
297
|
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
298
298
|
| `SKILL.md` | Agent entrypoint with YAML frontmatter and an embedded command reference table listing every command path, type (runnable/group), and documentation link. |
|
|
299
299
|
| `commands/*.md` | Per-command reference files. Leaf commands include usage, arguments, flags, defaults, and aliases. Group commands list subcommands with links. |
|
|
300
|
-
| `crust.json` | Crust-specific JSON metadata: name, description,
|
|
300
|
+
| `crust.json` | Crust-specific JSON metadata: name, description, and version. Also serves as an ownership marker — its presence indicates the skill was generated by Crust. |
|
|
301
301
|
|
|
302
302
|
## Conflict Detection
|
|
303
303
|
|
|
@@ -322,10 +322,16 @@ try {
|
|
|
322
322
|
|
|
323
323
|
After generating a skill bundle, consumers can install it by copying the skill directory.
|
|
324
324
|
|
|
325
|
-
### OpenCode
|
|
325
|
+
### Universal agents (OpenCode, Codex, Cursor, and others)
|
|
326
326
|
|
|
327
327
|
```sh
|
|
328
|
-
cp -r skills/use-my-cli/ .
|
|
328
|
+
cp -r skills/use-my-cli/ .agents/skills/use-my-cli/
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Global install for universal agents:
|
|
332
|
+
|
|
333
|
+
```sh
|
|
334
|
+
cp -r skills/use-my-cli/ ~/.config/agents/skills/use-my-cli/
|
|
329
335
|
```
|
|
330
336
|
|
|
331
337
|
### Claude Code
|
package/dist/index.d.ts
CHANGED
|
@@ -69,7 +69,9 @@ interface SkillMeta {
|
|
|
69
69
|
allowedTools?: string;
|
|
70
70
|
}
|
|
71
71
|
/** Supported agent targets for skill installation. */
|
|
72
|
-
type AgentTarget = "claude-code" | "opencode";
|
|
72
|
+
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";
|
|
73
|
+
/** Agent install class used by interactive skill management UX. */
|
|
74
|
+
type AgentClass = "universal" | "additional";
|
|
73
75
|
/** Installation scope — global (home directory) or project (cwd). */
|
|
74
76
|
type Scope = "global" | "project";
|
|
75
77
|
/**
|
|
@@ -241,44 +243,27 @@ interface SkillPluginOptions {
|
|
|
241
243
|
*/
|
|
242
244
|
command?: boolean | string;
|
|
243
245
|
}
|
|
244
|
-
/**
|
|
245
|
-
|
|
246
|
-
*/
|
|
246
|
+
/** Returns agents that use the canonical `.agents/skills` layout. */
|
|
247
|
+
declare function getUniversalAgents(): AgentTarget[];
|
|
248
|
+
/** Returns agents that use agent-specific skill roots. */
|
|
249
|
+
declare function getAdditionalAgents(): AgentTarget[];
|
|
250
|
+
/** Returns true if the agent uses the canonical `.agents/skills` layout. */
|
|
251
|
+
declare function isUniversalAgent(agent: AgentTarget): boolean;
|
|
247
252
|
interface DetectInstalledAgentsOptions {
|
|
248
|
-
/**
|
|
249
|
-
* Detection scope.
|
|
250
|
-
* - `global`: checks global config roots under home directory.
|
|
251
|
-
* - `project`: checks project-local config roots under cwd, then falls back
|
|
252
|
-
* to global roots under home directory when local roots are missing.
|
|
253
|
-
* @default "global"
|
|
254
|
-
*/
|
|
253
|
+
/** Kept for backwards compatibility with previous API. */
|
|
255
254
|
scope?: Scope;
|
|
256
|
-
/**
|
|
255
|
+
/** Kept for backwards compatibility with previous API. */
|
|
257
256
|
home?: string;
|
|
258
|
-
/** Working directory
|
|
257
|
+
/** Working directory for command checks. */
|
|
259
258
|
cwd?: string;
|
|
259
|
+
/** Test-only hook to override command detection. */
|
|
260
|
+
commandChecker?: (command: string, cwd: string) => Promise<boolean>;
|
|
260
261
|
}
|
|
261
262
|
/**
|
|
262
|
-
* Detects
|
|
263
|
-
* configuration roots for the requested scope.
|
|
263
|
+
* Detects installed additional agents by probing their CLI binaries.
|
|
264
264
|
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
* | --------- | ------------ | ------------------------------------------ |
|
|
268
|
-
* | `global` | `claude-code`| `<homedir>/.claude/` |
|
|
269
|
-
* | `global` | `opencode` | `<homedir>/.config/opencode/` |
|
|
270
|
-
* | `project` | `claude-code`| `<cwd>/.claude/`, fallback `<homedir>/.claude/` |
|
|
271
|
-
* | `project` | `opencode` | `<cwd>/.opencode/`, fallback `<homedir>/.config/opencode/` |
|
|
272
|
-
*
|
|
273
|
-
* @param options - Optional scope/home/cwd overrides. For backwards
|
|
274
|
-
* compatibility, passing a string is treated as `home`.
|
|
275
|
-
* @returns Array of detected agent targets (may be empty)
|
|
276
|
-
*
|
|
277
|
-
* @example
|
|
278
|
-
* ```ts
|
|
279
|
-
* const agents = await detectInstalledAgents();
|
|
280
|
-
* // ["claude-code"] — only Claude Code config found
|
|
281
|
-
* ```
|
|
265
|
+
* Universal agents are intentionally not detected here so callers can always
|
|
266
|
+
* present them as a single optional "Universal" install target.
|
|
282
267
|
*/
|
|
283
268
|
declare function detectInstalledAgents(options?: string | DetectInstalledAgentsOptions): Promise<AgentTarget[]>;
|
|
284
269
|
/** Details about the conflict between an existing skill and an incoming one. */
|
|
@@ -437,4 +422,4 @@ import { CrustPlugin } from "@crustjs/core";
|
|
|
437
422
|
* ```
|
|
438
423
|
*/
|
|
439
424
|
declare function skillPlugin(options: SkillPluginOptions): CrustPlugin;
|
|
440
|
-
export { uninstallSkill, skillStatus, skillPlugin, resolveSkillName, isValidSkillName, generateSkill, detectInstalledAgents, UninstallStatus, UninstallResult, UninstallOptions, StatusResult, StatusOptions, SkillPluginOptions, SkillMeta, SkillConflictError, SkillConflictDetails, Scope, InstallStatus, GenerateResult, GenerateOptions, AgentTarget, AgentResult };
|
|
425
|
+
export { uninstallSkill, skillStatus, skillPlugin, resolveSkillName, isValidSkillName, isUniversalAgent, getUniversalAgents, getAdditionalAgents, generateSkill, detectInstalledAgents, UninstallStatus, UninstallResult, UninstallOptions, StatusResult, StatusOptions, SkillPluginOptions, SkillMeta, SkillConflictError, SkillConflictDetails, Scope, InstallStatus, GenerateResult, GenerateOptions, AgentTarget, AgentResult, AgentClass };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{access as JQ}from"fs/promises";import{homedir as u}from"os";import{join as z}from"path";var qQ=["claude-code","opencode"],V={"claude-code":"Claude Code",opencode:"OpenCode"};function S(Q,Z,X){let H=Z==="global"?u():process.cwd();switch(Q){case"claude-code":return z(H,".claude","skills",X);case"opencode":if(Z==="global")return z(H,".config","opencode","skills",X);return z(H,".opencode","skills",X)}}async function E(Q){let Z=typeof Q==="string"?{home:Q}:Q??{},X=Z.scope??"global",H=Z.home??u(),W=Z.cwd??process.cwd(),$=[];for(let J of qQ){let q=xQ(J,X,H,W),R=!1;for(let B of q)if(R=await JQ(B).then(()=>!0).catch(()=>!1),R)break;if(R)$.push(J)}return $}function xQ(Q,Z,X,H){if(Z==="project")switch(Q){case"claude-code":return[z(H,".claude"),z(X,".claude")];case"opencode":return[z(H,".opencode"),z(X,".config","opencode")]}switch(Q){case"claude-code":return[z(X,".claude")];case"opencode":return[z(X,".config","opencode")]}}class j 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{access as n,mkdir as EQ,rm as a,writeFile as LQ}from"fs/promises";import{dirname as TQ,join as t}from"path";function p(Q){return f(Q,[])}function f(Q,Z){let X=RQ(Q.meta.name),H=[...Z,X],W=YQ(Q.args),$=KQ(Q.effectiveFlags),J=GQ(Q.subCommands,H);return{name:X,path:H,description:Q.meta.description,usage:Q.meta.usage,runnable:typeof Q.run==="function",args:W,flags:$,children:J}}function RQ(Q){return Q.trim().toLowerCase()}function YQ(Q){if(!Q||Q.length===0)return[];return Q.map(BQ)}function BQ(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=c(Q.default);return Z}function KQ(Q){if(!Q)return[];return Object.keys(Q).sort().map((X)=>{return OQ(X,Q[X])})}function OQ(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=c(Z.default);return X}function GQ(Q,Z){return Object.keys(Q).sort().map((H)=>{return f(Q[H],Z)})}function c(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function _(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 g(Q){return Q.replace(/(?<!\\)\|/g,"\\|")}function m(Q,Z){let X=[],H=d(Q);X.push({path:"SKILL.md",content:zQ(Q,Z,H)});for(let W of H){let $=M(W),J=W.children.length>0?MQ(W,Q):jQ(W,Q);X.push({path:$,content:J})}return X}function d(Q){let Z=[Q];for(let X of Q.children)Z.push(...d(X));return Z}function M(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function L(Q){return Q.path.join(" ")}function A(Q,Z){let X=Q.split("/").slice(0,-1),H=Z.split("/"),W=0;while(W<X.length&&W<H.length&&X[W]===H[W])W++;let $=X.length-W,J=H.slice(W);if($===0)return J.join("/");return[...Array.from({length:$},()=>".."),...J].join("/")}function zQ(Q,Z,X){let H=[];if(H.push("---"),H.push(`name: ${_(Z.name)}`),H.push(`description: ${_(Z.description)}`),Z.license)H.push(`license: ${_(Z.license)}`);if(Z.compatibility)H.push(`compatibility: ${_(Z.compatibility)}`);if(Z.disableModelInvocation)H.push("disable-model-invocation: true");if(Z.allowedTools)H.push(`allowed-tools: ${_(Z.allowedTools)}`);if(H.push("metadata:"),H.push(` version: "${Z.version}"`),H.push("---"),H.push(""),H.push(`# ${Z.name}`),H.push(""),Q.description)H.push(Q.description),H.push("");let W=Z.name.startsWith("use-")?Z.name.slice(4):Z.name;if(H.push(`Use this skill when working with \`${W}\` commands, or when you need help with \`${W}\` syntax, flags, or subcommands.`),H.push(""),H.push("## Command Reference"),H.push(""),H.push("This table lists all commands and their documentation paths. **Do not read all command files at once.** Instead:"),H.push(""),H.push("1. Use the table below to find the relevant command"),H.push("2. Use the `Type` column to choose what to execute: commands labeled `runnable` (including `runnable, group`) are executable, while `group` commands are not"),H.push("3. Read only the specific file from the `commands/` directory that you need"),H.push(""),H.push(...UQ(X)),H.push(""),Q.runnable){H.push("## Usage"),H.push("");let $=M(Q);H.push(`The root command is directly executable. See [${Q.name}](${$}) for usage details.`),H.push("")}return H.join(`
|
|
3
|
-
`)}function
|
|
4
|
-
`)}function
|
|
5
|
-
`)}function
|
|
6
|
-
`}
|
|
7
|
-
${
|
|
8
|
-
${
|
|
9
|
-
Skipped ${
|
|
10
|
-
${
|
|
2
|
+
import{spawn as kQ}from"child_process";import{homedir as XQ}from"os";import{join as $}from"path";var G=$(".agents","skills");function s(Q){if(Q!==XQ())return $(Q,".config");let Z=process.env.XDG_CONFIG_HOME?.trim();return Z&&Z.length>0?Z:$(Q,".config")}function U(Q){return $(s(Q),"agents","skills")}var P={amp:{label:"Amp",class:"universal",projectSkillsDir:G,globalSkillsDir:U},adal:{label:"AdaL",class:"additional",projectSkillsDir:$(".adal","skills"),globalSkillsDir:(Q)=>$(Q,".adal","skills"),detectCommands:["adal"]},antigravity:{label:"Antigravity",class:"additional",projectSkillsDir:$(".agent","skills"),globalSkillsDir:(Q)=>$(Q,".gemini","antigravity","skills"),detectCommands:["antigravity"]},augment:{label:"Augment",class:"additional",projectSkillsDir:$(".augment","skills"),globalSkillsDir:(Q)=>$(Q,".augment","skills"),detectCommands:["augment"]},"claude-code":{label:"Claude Code",class:"additional",projectSkillsDir:$(".claude","skills"),globalSkillsDir:(Q)=>$(process.env.CLAUDE_CONFIG_DIR?.trim()||$(Q,".claude"),"skills"),detectCommands:["claude","claude-code"]},cline:{label:"Cline",class:"universal",projectSkillsDir:G,globalSkillsDir:U},codebuddy:{label:"CodeBuddy",class:"additional",projectSkillsDir:$(".codebuddy","skills"),globalSkillsDir:(Q)=>$(Q,".codebuddy","skills"),detectCommands:["codebuddy"]},codex:{label:"Codex",class:"universal",projectSkillsDir:G,globalSkillsDir:U},"command-code":{label:"Command Code",class:"additional",projectSkillsDir:$(".commandcode","skills"),globalSkillsDir:(Q)=>$(Q,".commandcode","skills"),detectCommands:["command-code","commandcode"]},continue:{label:"Continue",class:"additional",projectSkillsDir:$(".continue","skills"),globalSkillsDir:(Q)=>$(Q,".continue","skills"),detectCommands:["continue"]},cortex:{label:"Cortex Code",class:"additional",projectSkillsDir:$(".cortex","skills"),globalSkillsDir:(Q)=>$(Q,".snowflake","cortex","skills"),detectCommands:["cortex"]},crush:{label:"Crush",class:"additional",projectSkillsDir:$(".crush","skills"),globalSkillsDir:(Q)=>$(s(Q),"crush","skills"),detectCommands:["crush"]},cursor:{label:"Cursor",class:"universal",projectSkillsDir:G,globalSkillsDir:U},droid:{label:"Droid",class:"additional",projectSkillsDir:$(".factory","skills"),globalSkillsDir:(Q)=>$(Q,".factory","skills"),detectCommands:["droid"]},"gemini-cli":{label:"Gemini CLI",class:"universal",projectSkillsDir:G,globalSkillsDir:U},"github-copilot":{label:"GitHub Copilot",class:"universal",projectSkillsDir:G,globalSkillsDir:U},goose:{label:"Goose",class:"additional",projectSkillsDir:$(".goose","skills"),globalSkillsDir:(Q)=>$(s(Q),"goose","skills"),detectCommands:["goose"]},"iflow-cli":{label:"iFlow CLI",class:"additional",projectSkillsDir:$(".iflow","skills"),globalSkillsDir:(Q)=>$(Q,".iflow","skills"),detectCommands:["iflow","iflow-cli"]},junie:{label:"Junie",class:"additional",projectSkillsDir:$(".junie","skills"),globalSkillsDir:(Q)=>$(Q,".junie","skills"),detectCommands:["junie"]},kilo:{label:"Kilo Code",class:"additional",projectSkillsDir:$(".kilocode","skills"),globalSkillsDir:(Q)=>$(Q,".kilocode","skills"),detectCommands:["kilo","kilocode"]},"kimi-cli":{label:"Kimi Code CLI",class:"universal",projectSkillsDir:G,globalSkillsDir:U},"kiro-cli":{label:"Kiro CLI",class:"additional",projectSkillsDir:$(".kiro","skills"),globalSkillsDir:(Q)=>$(Q,".kiro","skills"),detectCommands:["kiro","kiro-cli"]},kode:{label:"Kode",class:"additional",projectSkillsDir:$(".kode","skills"),globalSkillsDir:(Q)=>$(Q,".kode","skills"),detectCommands:["kode"]},mcpjam:{label:"MCPJam",class:"additional",projectSkillsDir:$(".mcpjam","skills"),globalSkillsDir:(Q)=>$(Q,".mcpjam","skills"),detectCommands:["mcpjam"]},"mistral-vibe":{label:"Mistral Vibe",class:"additional",projectSkillsDir:$(".vibe","skills"),globalSkillsDir:(Q)=>$(Q,".vibe","skills"),detectCommands:["mistral-vibe","vibe"]},mux:{label:"Mux",class:"additional",projectSkillsDir:$(".mux","skills"),globalSkillsDir:(Q)=>$(Q,".mux","skills"),detectCommands:["mux"]},neovate:{label:"Neovate",class:"additional",projectSkillsDir:$(".neovate","skills"),globalSkillsDir:(Q)=>$(Q,".neovate","skills"),detectCommands:["neovate"]},opencode:{label:"OpenCode",class:"universal",projectSkillsDir:G,globalSkillsDir:U},openclaw:{label:"OpenClaw",class:"additional",projectSkillsDir:"skills",globalSkillsDir:(Q)=>$(Q,".openclaw","skills"),detectCommands:["openclaw"]},openhands:{label:"OpenHands",class:"additional",projectSkillsDir:$(".openhands","skills"),globalSkillsDir:(Q)=>$(Q,".openhands","skills"),detectCommands:["openhands"]},pi:{label:"Pi",class:"additional",projectSkillsDir:$(".pi","skills"),globalSkillsDir:(Q)=>$(Q,".pi","agent","skills"),detectCommands:["pi"]},pochi:{label:"Pochi",class:"additional",projectSkillsDir:$(".pochi","skills"),globalSkillsDir:(Q)=>$(Q,".pochi","skills"),detectCommands:["pochi"]},qoder:{label:"Qoder",class:"additional",projectSkillsDir:$(".qoder","skills"),globalSkillsDir:(Q)=>$(Q,".qoder","skills"),detectCommands:["qoder"]},"qwen-code":{label:"Qwen Code",class:"additional",projectSkillsDir:$(".qwen","skills"),globalSkillsDir:(Q)=>$(Q,".qwen","skills"),detectCommands:["qwen","qwen-code"]},replit:{label:"Replit",class:"universal",projectSkillsDir:G,globalSkillsDir:U},roo:{label:"Roo Code",class:"additional",projectSkillsDir:$(".roo","skills"),globalSkillsDir:(Q)=>$(Q,".roo","skills"),detectCommands:["roo","roo-code"]},trae:{label:"Trae",class:"additional",projectSkillsDir:$(".trae","skills"),globalSkillsDir:(Q)=>$(Q,".trae","skills"),detectCommands:["trae"]},"trae-cn":{label:"Trae CN",class:"additional",projectSkillsDir:$(".trae","skills"),globalSkillsDir:(Q)=>$(Q,".trae-cn","skills"),detectCommands:["trae-cn","trae"]},windsurf:{label:"Windsurf",class:"additional",projectSkillsDir:$(".windsurf","skills"),globalSkillsDir:(Q)=>$(Q,".codeium","windsurf","skills"),detectCommands:["windsurf"]},zencoder:{label:"Zencoder",class:"additional",projectSkillsDir:$(".zencoder","skills"),globalSkillsDir:(Q)=>$(Q,".zencoder","skills"),detectCommands:["zencoder"]}},t=Object.keys(P),k=Object.fromEntries(t.map((Q)=>[Q,P[Q].label]));function f(){return t.filter((Q)=>P[Q].class==="universal")}function h(){return t.filter((Q)=>P[Q].class==="additional")}function yQ(Q){return P[Q].class==="universal"}async function p(Q){let Z=typeof Q==="string"?{home:Q}:Q??{},X=Z.cwd??process.cwd(),W=Z.commandChecker??PQ,H=[];for(let q of h()){let B=P[q].detectCommands??[],x=!1;for(let z of B)if(await W(z,X)){x=!0;break}if(x)H.push(q)}return H}function u(Q,Z,X){let W=P[Q];if(Z==="project")return $(process.cwd(),W.projectSkillsDir,X);return $(W.globalSkillsDir(XQ()),X)}async function PQ(Q,Z){for(let X of[["--version"],["-v"],["version"]])if(await bQ(Q,X,Z))return!0;return!1}async function bQ(Q,Z,X){return new Promise((W)=>{let H=kQ(Q,Z,{cwd:X,stdio:"ignore",shell:!1}),q=!1,B=(z)=>{if(q)return;q=!0,W(z)},x=setTimeout(()=>{H.kill("SIGTERM"),B(!1)},1000);H.once("error",()=>{clearTimeout(x),B(!1)}),H.once("exit",(z)=>{clearTimeout(x),B(z===0)})})}class b 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{access as MQ,mkdir as gQ,rm as wQ,writeFile as mQ}from"fs/promises";import{dirname as dQ,join as OQ}from"path";function ZQ(Q){return WQ(Q,[])}function WQ(Q,Z){let X=TQ(Q.meta.name),W=[...Z,X],H=CQ(Q.args),q=_Q(Q.effectiveFlags),B=DQ(Q.subCommands,W);return{name:X,path:W,description:Q.meta.description,usage:Q.meta.usage,runnable:typeof Q.run==="function",args:H,flags:q,children:B}}function TQ(Q){return Q.trim().toLowerCase()}function CQ(Q){if(!Q||Q.length===0)return[];return Q.map(IQ)}function IQ(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=$Q(Q.default);return Z}function _Q(Q){if(!Q)return[];return Object.keys(Q).sort().map((X)=>{return EQ(X,Q[X])})}function EQ(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=$Q(Z.default);return X}function DQ(Q,Z){return Object.keys(Q).sort().map((W)=>{return WQ(Q[W],Z)})}function $Q(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function N(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 qQ(Q,Z){let X=[],W=YQ(Q);X.push({path:"SKILL.md",content:LQ(Q,Z,W)});for(let H of W){let q=T(H),B=H.children.length>0?vQ(H,Q):SQ(H,Q);X.push({path:q,content:B})}return X}function YQ(Q){let Z=[Q];for(let X of Q.children)Z.push(...YQ(X));return Z}function T(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function c(Q){return Q.path.join(" ")}function o(Q,Z){let X=Q.split("/").slice(0,-1),W=Z.split("/"),H=0;while(H<X.length&&H<W.length&&X[H]===W[H])H++;let q=X.length-H,B=W.slice(H);if(q===0)return B.join("/");return[...Array.from({length:q},()=>".."),...B].join("/")}function LQ(Q,Z,X){let W=[];if(W.push("---"),W.push(`name: ${N(Z.name)}`),W.push(`description: ${N(Z.description)}`),Z.license)W.push(`license: ${N(Z.license)}`);if(Z.compatibility)W.push(`compatibility: ${N(Z.compatibility)}`);if(Z.disableModelInvocation)W.push("disable-model-invocation: true");if(Z.allowedTools)W.push(`allowed-tools: ${N(Z.allowedTools)}`);if(W.push("metadata:"),W.push(` version: "${Z.version}"`),W.push("---"),W.push(""),W.push(`# ${Z.name}`),W.push(""),Q.description)W.push(Q.description),W.push("");let H=Z.name.startsWith("use-")?Z.name.slice(4):Z.name;if(W.push(`Use this skill when working with \`${H}\` commands, or when you need help with \`${H}\` syntax, flags, or subcommands.`),W.push(""),W.push("## Command Reference"),W.push(""),W.push("This table lists all commands and their documentation paths. **Do not read all command files at once.** Instead:"),W.push(""),W.push("1. Use the table below to find the relevant command"),W.push("2. Use the `Type` column to choose what to execute: commands labeled `runnable` (including `runnable, group`) are executable, while `group` commands are not"),W.push("3. Read only the specific file from the `commands/` directory that you need"),W.push(""),W.push(...jQ(X)),W.push(""),Q.runnable){W.push("## Usage"),W.push("");let q=T(Q);W.push(`The root command is directly executable. See [${Q.name}](${q}) for usage details.`),W.push("")}return W.join(`
|
|
3
|
+
`)}function jQ(Q){let Z=[];Z.push("| Command | Type | Documentation |"),Z.push("| ------- | ---- | ------------- |");for(let X of Q){let W=c(X),H=T(X),q=NQ(X);Z.push(`| \`${W}\` | ${q} | [${H}](${H}) |`)}return Z}function NQ(Q){if(Q.runnable&&Q.children.length>0)return"runnable, group";if(Q.runnable)return"runnable";return"group"}function SQ(Q,Z){let X=[],W=c(Q);if(X.push(`# \`${W}\``),X.push(""),Q.description)X.push(Q.description),X.push("");if(X.push("## Usage"),X.push(""),Q.usage)X.push("```"),X.push(Q.usage),X.push("```");else X.push("```"),X.push(BQ(Q)),X.push("```");if(X.push(""),Q.args.length>0)X.push("## Arguments"),X.push(""),X.push(...xQ(Q.args)),X.push("");if(Q.flags.length>0)X.push("## Flags"),X.push(""),X.push(...zQ(Q.flags)),X.push("");return X.push(...JQ(Q,Z)),X.join(`
|
|
4
|
+
`)}function vQ(Q,Z){let X=[],W=c(Q),H=T(Q);if(X.push(`# \`${W}\``),X.push(""),Q.description)X.push(Q.description),X.push("");if(Q.runnable){if(X.push("## Usage"),X.push(""),Q.usage)X.push("```"),X.push(Q.usage),X.push("```");else X.push("```"),X.push(BQ(Q)),X.push("```");if(X.push(""),Q.args.length>0)X.push("## Arguments"),X.push(""),X.push(...xQ(Q.args)),X.push("");if(Q.flags.length>0)X.push("## Flags"),X.push(""),X.push(...zQ(Q.flags)),X.push("")}X.push("## Subcommands"),X.push("");for(let q of Q.children){let B=T(q),x=o(H,B),z=q.description?` - ${q.description}`:"";X.push(`- [\`${q.name}\`](${x})${z}`)}return X.push(""),X.push(...JQ(Q,Z)),X.join(`
|
|
5
|
+
`)}function BQ(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 xQ(Q){let Z=[];Z.push("| Argument | Type | Required | Description |"),Z.push("| -------- | ---- | -------- | ----------- |");for(let X of Q){let W=X.variadic?`${X.name}...`:X.name,H=X.required?"Yes":"No",q=HQ(AQ(X));Z.push(`| \`${W}\` | ${X.type} | ${H} | ${q} |`)}return Z}function AQ(Q){let Z=[];if(Q.description)Z.push(Q.description);if(Q.default!==void 0)Z.push(`Default: \`${Q.default}\``);return Z.join(". ")||"-"}function zQ(Q){let Z=[];Z.push("| Flag | Type | Required | Description |"),Z.push("| ---- | ---- | -------- | ----------- |");for(let X of Q){let W=fQ(X),H=X.required?"Yes":"No",q=HQ(hQ(X));Z.push(`| ${W} | ${X.type} | ${H} | ${q} |`)}return Z}function fQ(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 hQ(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 JQ(Q,Z){let X=[],W=T(Q);if(X.push("---"),X.push(""),Q.path.length>1){let q=Q.path.slice(0,-1),B=KQ(Z,q);if(B){let x=T(B),z=o(W,x),J=c(B);X.push(`Parent: [\`${J}\`](${z})`),X.push("")}}let H=o(W,"SKILL.md");return X.push(`[Skill Overview](${H})`),X.push(""),X}function KQ(Q,Z){if(pQ(Q.path,Z))return Q;for(let X of Q.children){let W=KQ(X,Z);if(W)return W}return}function pQ(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 uQ}from"fs/promises";import{join as cQ}from"path";var a="crust.json";async function n(Q){try{let Z=await uQ(cQ(Q,a),"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 FQ=/^[a-z0-9]+(-[a-z0-9]+)*$/;function VQ(Q){return Q.length>=1&&Q.length<=64&&FQ.test(Q)}function _(Q){return Q.startsWith("use-")?Q:`use-${Q}`}async function S(Q){let{command:Z,meta:X,agents:W,scope:H="global",clean:q=!0,force:B=!1}=Q,x=_(X.name);if(!VQ(x))throw Error(`Invalid skill name "${x}": must be 1\u201364 lowercase `+`alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens. Pattern: ${FQ.source}`);let z={...X,name:x},J=ZQ(Z),O=qQ(J,z),E=rQ(z),D=[...O,...E].sort((K,M)=>K.path<M.path?-1:K.path>M.path?1:0),L=[],V=new Map;for(let K of W){let M=u(K,H,z.name),R=V.get(M);if(R)R.push(K);else V.set(M,[K])}for(let[K,M]of V){let R=M[0];if(!R)continue;let y=await n(K);if(y===null){if(await MQ(K).then(()=>!0).catch(()=>!1)&&!B)throw new b({agent:R,outputDir:K})}let C=y===null?"installed":y===z.version?"up-to-date":"updated";if(C==="up-to-date"){for(let I of M)L.push({agent:I,outputDir:K,files:[],status:"up-to-date"});continue}let j=C==="updated"?y??void 0:void 0;if(q)await iQ(K);await sQ(K,D);for(let I of M)L.push({agent:I,outputDir:K,files:D.map((r)=>r.path),status:C,previousVersion:j})}return{agents:L}}async function e(Q){let{name:Z,agents:X,scope:W="global"}=Q,H=_(Z),q=[],B=new Map;for(let x of X){let z=u(x,W,H),J=B.get(z);if(J)J.push(x);else B.set(z,[x])}for(let[x,z]of B)if(await MQ(x).then(()=>!0).catch(()=>!1)){await wQ(x,{recursive:!0,force:!0});for(let O of z)q.push({agent:O,outputDir:x,status:"removed"})}else for(let O of z)q.push({agent:O,outputDir:x,status:"not-found"});return{agents:q}}async function g(Q){let{name:Z,agents:X,scope:W="global"}=Q,H=_(Z),q=[],B=new Map;for(let x of X){let z=u(x,W,H),J=B.get(z);if(J)J.push(x);else B.set(z,[x])}for(let[x,z]of B){let J=await n(x);for(let O of z)q.push({agent:O,outputDir:x,installed:J!==null,version:J??void 0})}return{agents:q}}function rQ(Q){return[{path:a,content:lQ(Q)}]}function lQ(Q){let Z={name:Q.name,description:Q.description,version:Q.version};return`${JSON.stringify(Z,null,"\t")}
|
|
6
|
+
`}async function iQ(Q){await wQ(Q,{recursive:!0,force:!0})}async function sQ(Q,Z){let X=new Set;for(let H of Z){let q=OQ(Q,H.path),B=dQ(q);X.add(B)}let W=[...X].sort();for(let H of W)await gQ(H,{recursive:!0});for(let H of Z){let q=OQ(Q,H.path);await mQ(q,H.content,"utf-8")}}import{createCommandNode as tQ,VALIDATION_MODE_ENV as oQ}from"@crustjs/core";import{confirm as aQ,multiselect as nQ,spinner as d}from"@crustjs/prompts";import{bold as QQ,dim as v,yellow as eQ}from"@crustjs/style";var GQ="skill",UQ="global",m="__universal__";function RQ(Q,Z){return{name:Q.meta.name,description:Q.meta.description??"",version:Z}}async function QX(Q,Z){let X=Z.scope??UQ,W=[...f(),...await p({scope:X})];if(W.length===0)return;let H=RQ(Q,Z.version),B=(await g({name:H.name,agents:W,scope:X})).agents.filter((x)=>x.installed&&x.version!==H.version);if(B.length===0)return;try{await d({message:"Updating skills...",task:async({updateMessage:x})=>{let z=await S({command:Q,meta:H,agents:B.map((O)=>O.agent),scope:Z.scope}),J=z.agents.filter((O)=>O.status==="updated").map((O)=>k[O.agent]);if(J.length>0)x(`Updated skill "${_(H.name)}" to v${H.version} for ${J.join(", ")}`);return z}})}catch(x){if(x instanceof b)console.warn(eQ(`Skill conflict: "${x.details.outputDir}" already exists but was not created by ${H.name}. Skipping auto-update. Delete or rename the conflicting skill to resolve.`));else throw x}}function XX(Q){let Z;return{name:"skills",async setup(X,W){Z=X.rootCommand;let H=typeof Q.command==="string"?Q.command:GQ;if(Q.command!==!1)W.addSubCommand(Z,H,ZX(Z,Q));if(process.env[oQ]==="1")return;if(Q.command!==!1&&X.argv[0]===H)return;if(Q.autoUpdate!==!1)await QX(Z,Q)}}}function ZX(Q,Z){let X=tQ(GQ);return X.meta.description="Manage agent skill installations",X.run=async()=>{let W=RQ(Q,Z.version),H=Z.scope??UQ,q=await p({scope:H}),B=f(),x=h(),z=await g({name:W.name,agents:[...B,...x],scope:H}),J=new Set(z.agents.filter((Y)=>Y.installed).map((Y)=>Y.agent)),O=new Set(q),E=new Map(z.agents.map((Y)=>[Y.agent,Y])),D=x.filter((Y)=>{if(O.has(Y))return!0;return E.get(Y)?.installed===!0}),L=D.filter((Y)=>J.has(Y)),V=[];if(B.length>0){let Y=B[0];if(!Y)throw Error("Expected at least one universal agent");let F=E.get(Y)?.outputDir??"path unavailable";V.push({label:"Universal",value:m,hint:F}),console.log(v(`Universal includes: ${B.map((A)=>k[A]).join(", ")}`))}for(let Y of D){let F=E.get(Y)?.outputDir??"path unavailable";V.push({label:k[Y],value:Y,hint:F})}let K=B.length>0&&B.every((Y)=>J.has(Y)),M=[...L.filter((Y)=>!B.includes(Y))];if(K)M.unshift(m);let R=process.stdin.isTTY,y=V.length===0?[]:await nQ({message:"Select agents to install skills for",choices:V,default:M,initial:!R?V.map((Y)=>Y.value):void 0,required:!1}),C=new Set(y.filter((Y)=>Y!==m));if(y.includes(m))for(let Y of B)C.add(Y);let j=[...C],I=j.filter((Y)=>!J.has(Y)),r=j.filter((Y)=>{let w=z.agents.find((F)=>F.agent===Y);return w?.installed===!0&&w.version!==W.version}),l=[...J].filter((Y)=>!j.includes(Y)),i=[...I,...r];if(i.length>0)try{let Y=await d({message:"Installing skills...",task:async()=>S({command:Q,meta:W,agents:i,scope:H})});console.log(`
|
|
7
|
+
${QQ(`Installed "${W.name}" v${W.version}`)}`);for(let w of Y.agents)console.log(v(` ${k[w.agent]} \u2192 ${w.outputDir}`))}catch(Y){if(Y instanceof b)if(await aQ({message:`"${Y.details.outputDir}" already exists but was not created by Crust. Overwrite?`,default:!1,initial:!R?!1:void 0})){let F=await d({message:"Overwriting skill...",task:async()=>S({command:Q,meta:W,agents:[Y.details.agent],scope:H,force:!0})});console.log(`
|
|
8
|
+
${QQ(`Installed "${W.name}" v${W.version}`)}`);for(let A of F.agents)console.log(v(` ${k[A.agent]} \u2192 ${A.outputDir}`))}else console.log(v(`
|
|
9
|
+
Skipped ${k[Y.details.agent]}`));else throw Y}if(l.length>0){let w=(await d({message:"Removing skills...",task:async()=>e({name:W.name,agents:l,scope:H})})).agents.filter((F)=>F.status==="removed").map((F)=>k[F.agent]);if(w.length>0)console.log(`
|
|
10
|
+
${QQ(`Removed from ${w.join(", ")}`)}`)}if(i.length===0&&l.length===0)console.log(v("No changes."))},X}export{e as uninstallSkill,g as skillStatus,XX as skillPlugin,_ as resolveSkillName,VQ as isValidSkillName,yQ as isUniversalAgent,f as getUniversalAgents,h as getAdditionalAgents,S as generateSkill,p as detectInstalledAgents,b as SkillConflictError};
|