@crustjs/skills 0.0.14 → 0.0.16
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 +23 -1
- package/dist/index.d.ts +32 -12
- package/dist/index.js +10 -10
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -62,6 +62,7 @@ runMain(app, {
|
|
|
62
62
|
// autoUpdate: true (default) — silently updates installed skills
|
|
63
63
|
// command: "skill" (default) — registers "my-cli skill" subcommand
|
|
64
64
|
// defaultScope: "global" | "project" — skip scope prompt when set
|
|
65
|
+
// installMode: "auto" | "symlink" | "copy" (default: "auto")
|
|
65
66
|
}),
|
|
66
67
|
],
|
|
67
68
|
});
|
|
@@ -69,6 +70,8 @@ runMain(app, {
|
|
|
69
70
|
|
|
70
71
|
The plugin automatically updates already-installed skills when the version changes, checking both project and global paths for the current working directory. First-time installation is done via the interactive `skill` subcommand (or `skill update` for update-only flows), or programmatically using the exported primitives.
|
|
71
72
|
|
|
73
|
+
Generated bundles are written once to a canonical store (`.crust/skills` for project scope, `~/.crust/skills` for global scope) and then installed into agent paths via symlink or copy depending on `installMode`.
|
|
74
|
+
|
|
72
75
|
### Programmatic Auto-Install
|
|
73
76
|
|
|
74
77
|
For full control over first-time installation, use the exported primitives
|
|
@@ -191,6 +194,7 @@ const result = await generateSkill({
|
|
|
191
194
|
meta: { name: "my-cli", description: "My CLI tool", version: "1.0.0" },
|
|
192
195
|
agents: ["opencode"],
|
|
193
196
|
scope: "project", // default: "global"
|
|
197
|
+
installMode: "auto", // default: "auto" — symlink first, fallback to copy
|
|
194
198
|
clean: true, // default: true — removes existing skill dir first
|
|
195
199
|
force: false, // default: false — throws SkillConflictError if dir exists without crust.json
|
|
196
200
|
});
|
|
@@ -225,6 +229,20 @@ for (const file of files) {
|
|
|
225
229
|
}
|
|
226
230
|
```
|
|
227
231
|
|
|
232
|
+
### `resolveCanonicalSkillPath(scope, name)`
|
|
233
|
+
|
|
234
|
+
Resolves the canonical store path where Crust writes the single source-of-truth skill bundle. Agent install paths are symlinked (or copied) from this location.
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import { resolveCanonicalSkillPath } from "@crustjs/skills";
|
|
238
|
+
|
|
239
|
+
resolveCanonicalSkillPath("project", "use-my-cli");
|
|
240
|
+
// → "<cwd>/.crust/skills/use-my-cli"
|
|
241
|
+
|
|
242
|
+
resolveCanonicalSkillPath("global", "use-my-cli");
|
|
243
|
+
// → "~/.crust/skills/use-my-cli"
|
|
244
|
+
```
|
|
245
|
+
|
|
228
246
|
### `isValidSkillName(name)`
|
|
229
247
|
|
|
230
248
|
Validates a skill name against the [Agent Skills spec](https://agentskills.io/specification) pattern: 1–64 lowercase alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens.
|
|
@@ -302,6 +320,10 @@ skills/use-my-cli/
|
|
|
302
320
|
|
|
303
321
|
Each skill directory contains a `crust.json` file that acts as an ownership marker. If `generateSkill()` encounters an existing directory without `crust.json`, it throws a `SkillConflictError` to prevent overwriting skills created manually or by other tools.
|
|
304
322
|
|
|
323
|
+
### Uninstall Cleanup
|
|
324
|
+
|
|
325
|
+
When `uninstallSkill()` removes agent install paths, it also checks whether any other agent paths still reference the skill. If no agent installs remain, the canonical store entry (`.crust/skills/<skill>` or `~/.crust/skills/<skill>`) is automatically removed.
|
|
326
|
+
|
|
305
327
|
Pass `force: true` to overwrite, or handle the error:
|
|
306
328
|
|
|
307
329
|
```ts
|
|
@@ -330,7 +352,7 @@ cp -r skills/use-my-cli/ .agents/skills/use-my-cli/
|
|
|
330
352
|
Global install for universal agents:
|
|
331
353
|
|
|
332
354
|
```sh
|
|
333
|
-
cp -r skills/use-my-cli/ ~/.
|
|
355
|
+
cp -r skills/use-my-cli/ ~/.agents/skills/use-my-cli/
|
|
334
356
|
```
|
|
335
357
|
|
|
336
358
|
### Claude Code
|
package/dist/index.d.ts
CHANGED
|
@@ -74,6 +74,8 @@ type AgentTarget = "amp" | "adal" | "antigravity" | "augment" | "claude-code" |
|
|
|
74
74
|
type AgentClass = "universal" | "additional";
|
|
75
75
|
/** Installation scope — global (home directory) or project (cwd). */
|
|
76
76
|
type Scope = "global" | "project";
|
|
77
|
+
/** Installation strategy for agent skill output paths. */
|
|
78
|
+
type SkillInstallMode = "auto" | "symlink" | "copy";
|
|
77
79
|
/**
|
|
78
80
|
* Top-level options for generating a skill bundle from a command tree.
|
|
79
81
|
*
|
|
@@ -105,6 +107,19 @@ interface GenerateOptions {
|
|
|
105
107
|
/** Agent targets to install skills for */
|
|
106
108
|
agents: AgentTarget[];
|
|
107
109
|
/**
|
|
110
|
+
* Installation strategy for agent output paths.
|
|
111
|
+
*
|
|
112
|
+
* - `"auto"` (default): create a symlink to the canonical `.crust/skills`
|
|
113
|
+
* bundle, falling back to a hard copy when symlinks are unavailable.
|
|
114
|
+
* - `"symlink"`: require symlinks; fail if a symlink cannot be created.
|
|
115
|
+
* - `"copy"`: write full copies directly into each agent path.
|
|
116
|
+
*
|
|
117
|
+
* Canonical bundles are always generated once under `.crust/skills` (project)
|
|
118
|
+
* or `~/.crust/skills` (global).
|
|
119
|
+
* @default "auto"
|
|
120
|
+
*/
|
|
121
|
+
installMode?: SkillInstallMode;
|
|
122
|
+
/**
|
|
108
123
|
* Installation scope — global (home directory) or project (cwd).
|
|
109
124
|
* @default "global"
|
|
110
125
|
*/
|
|
@@ -229,6 +244,11 @@ interface SkillPluginOptions {
|
|
|
229
244
|
*/
|
|
230
245
|
defaultScope?: Scope;
|
|
231
246
|
/**
|
|
247
|
+
* Installation strategy used when the plugin calls `generateSkill()`.
|
|
248
|
+
* @default "auto"
|
|
249
|
+
*/
|
|
250
|
+
installMode?: SkillInstallMode;
|
|
251
|
+
/**
|
|
232
252
|
* Automatically update skills when the installed version is outdated.
|
|
233
253
|
* @default true
|
|
234
254
|
*/
|
|
@@ -257,18 +277,22 @@ interface DetectInstalledAgentsOptions {
|
|
|
257
277
|
scope?: Scope;
|
|
258
278
|
/** Kept for backwards compatibility with previous API. */
|
|
259
279
|
home?: string;
|
|
260
|
-
/** Working directory for
|
|
280
|
+
/** Working directory for PATH lookups. */
|
|
261
281
|
cwd?: string;
|
|
262
282
|
/** Test-only hook to override command detection. */
|
|
263
283
|
commandChecker?: (command: string, cwd: string) => Promise<boolean>;
|
|
264
284
|
}
|
|
265
285
|
/**
|
|
266
|
-
* Detects installed additional agents by
|
|
286
|
+
* Detects installed additional agents by checking PATH for their CLI binaries.
|
|
267
287
|
*
|
|
268
288
|
* Universal agents are intentionally not detected here so callers can always
|
|
269
289
|
* present them as a single optional "Universal" install target.
|
|
270
290
|
*/
|
|
271
291
|
declare function detectInstalledAgents(options?: string | DetectInstalledAgentsOptions): Promise<AgentTarget[]>;
|
|
292
|
+
/**
|
|
293
|
+
* Resolves the canonical skill bundle path used by Crust.
|
|
294
|
+
*/
|
|
295
|
+
declare function resolveCanonicalSkillPath(scope: Scope, name: string): string;
|
|
272
296
|
/** Details about the conflict between an existing skill and an incoming one. */
|
|
273
297
|
interface SkillConflictDetails {
|
|
274
298
|
/** The agent where the conflict was detected */
|
|
@@ -329,14 +353,10 @@ declare function resolveSkillName(name: string): string;
|
|
|
329
353
|
/**
|
|
330
354
|
* Generates and installs agent skill bundles from a Crust command tree.
|
|
331
355
|
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
* 3. Checks the installed version — skips if up-to-date
|
|
337
|
-
* 4. Builds a canonical manifest from the command tree
|
|
338
|
-
* 5. Renders markdown files + `crust.json`
|
|
339
|
-
* 6. Writes files to the agent's skill directory
|
|
356
|
+
* The generator renders the bundle once into a canonical Crust store
|
|
357
|
+
* (`.crust/skills` project scope, `~/.crust/skills` global scope), then
|
|
358
|
+
* installs into agent-specific output paths using the configured install mode
|
|
359
|
+
* (`auto`, `symlink`, `copy`).
|
|
340
360
|
*
|
|
341
361
|
* @param options - Generation options including command, metadata, agents, and scope
|
|
342
362
|
* @returns Per-agent installation results
|
|
@@ -416,10 +436,10 @@ import { CrustPlugin } from "@crustjs/core";
|
|
|
416
436
|
* version: "1.0.0",
|
|
417
437
|
* command: "skill", // registers "my-cli skill" subcommand
|
|
418
438
|
* }))
|
|
419
|
-
* .run(() => { /* ...
|
|
439
|
+
* .run(() => { /* ... *�/ });
|
|
420
440
|
*
|
|
421
441
|
* await app.execute();
|
|
422
442
|
* ```
|
|
423
443
|
*/
|
|
424
444
|
declare function skillPlugin(options: SkillPluginOptions): CrustPlugin;
|
|
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 };
|
|
445
|
+
export { uninstallSkill, skillStatus, skillPlugin, resolveSkillName, resolveCanonicalSkillPath, isValidSkillName, isUniversalAgent, getUniversalAgents, getAdditionalAgents, generateSkill, detectInstalledAgents, UninstallStatus, UninstallResult, UninstallOptions, StatusResult, StatusOptions, SkillPluginOptions, SkillMeta, SkillInstallMode, SkillConflictError, SkillConflictDetails, Scope, InstallStatus, GenerateResult, GenerateOptions, AgentTarget, AgentResult, AgentClass };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{spawn as IQ}from"child_process";import{homedir as WQ}from"os";import{join as H}from"path";var R=H(".agents","skills");function ZQ(Q){if(Q!==WQ())return H(Q,".config");let Z=process.env.XDG_CONFIG_HOME?.trim();return Z&&Z.length>0?Z:H(Q,".config")}function y(Q){return H(Q,".agents","skills")}var _={amp:{label:"Amp",class:"universal",projectSkillsDir:R,globalSkillsDir:y},adal:{label:"AdaL",class:"additional",projectSkillsDir:H(".adal","skills"),globalSkillsDir:(Q)=>H(Q,".adal","skills"),detectCommands:["adal"]},antigravity:{label:"Antigravity",class:"additional",projectSkillsDir:H(".agent","skills"),globalSkillsDir:(Q)=>H(Q,".gemini","antigravity","skills"),detectCommands:["antigravity"]},augment:{label:"Augment",class:"additional",projectSkillsDir:H(".augment","skills"),globalSkillsDir:(Q)=>H(Q,".augment","skills"),detectCommands:["augment"]},"claude-code":{label:"Claude Code",class:"additional",projectSkillsDir:H(".claude","skills"),globalSkillsDir:(Q)=>H(process.env.CLAUDE_CONFIG_DIR?.trim()||H(Q,".claude"),"skills"),detectCommands:["claude","claude-code"]},cline:{label:"Cline",class:"universal",projectSkillsDir:R,globalSkillsDir:y},codebuddy:{label:"CodeBuddy",class:"additional",projectSkillsDir:H(".codebuddy","skills"),globalSkillsDir:(Q)=>H(Q,".codebuddy","skills"),detectCommands:["codebuddy"]},codex:{label:"Codex",class:"universal",projectSkillsDir:R,globalSkillsDir:y},"command-code":{label:"Command Code",class:"additional",projectSkillsDir:H(".commandcode","skills"),globalSkillsDir:(Q)=>H(Q,".commandcode","skills"),detectCommands:["command-code","commandcode"]},continue:{label:"Continue",class:"additional",projectSkillsDir:H(".continue","skills"),globalSkillsDir:(Q)=>H(Q,".continue","skills"),detectCommands:["continue"]},cortex:{label:"Cortex Code",class:"additional",projectSkillsDir:H(".cortex","skills"),globalSkillsDir:(Q)=>H(Q,".snowflake","cortex","skills"),detectCommands:["cortex"]},crush:{label:"Crush",class:"additional",projectSkillsDir:H(".crush","skills"),globalSkillsDir:(Q)=>H(ZQ(Q),"crush","skills"),detectCommands:["crush"]},cursor:{label:"Cursor",class:"universal",projectSkillsDir:R,globalSkillsDir:y},droid:{label:"Droid",class:"additional",projectSkillsDir:H(".factory","skills"),globalSkillsDir:(Q)=>H(Q,".factory","skills"),detectCommands:["droid"]},"gemini-cli":{label:"Gemini CLI",class:"universal",projectSkillsDir:R,globalSkillsDir:y},"github-copilot":{label:"GitHub Copilot",class:"universal",projectSkillsDir:R,globalSkillsDir:y},goose:{label:"Goose",class:"additional",projectSkillsDir:H(".goose","skills"),globalSkillsDir:(Q)=>H(ZQ(Q),"goose","skills"),detectCommands:["goose"]},"iflow-cli":{label:"iFlow CLI",class:"additional",projectSkillsDir:H(".iflow","skills"),globalSkillsDir:(Q)=>H(Q,".iflow","skills"),detectCommands:["iflow","iflow-cli"]},junie:{label:"Junie",class:"additional",projectSkillsDir:H(".junie","skills"),globalSkillsDir:(Q)=>H(Q,".junie","skills"),detectCommands:["junie"]},kilo:{label:"Kilo Code",class:"additional",projectSkillsDir:H(".kilocode","skills"),globalSkillsDir:(Q)=>H(Q,".kilocode","skills"),detectCommands:["kilo","kilocode"]},"kimi-cli":{label:"Kimi Code CLI",class:"universal",projectSkillsDir:R,globalSkillsDir:y},"kiro-cli":{label:"Kiro CLI",class:"additional",projectSkillsDir:H(".kiro","skills"),globalSkillsDir:(Q)=>H(Q,".kiro","skills"),detectCommands:["kiro","kiro-cli"]},kode:{label:"Kode",class:"additional",projectSkillsDir:H(".kode","skills"),globalSkillsDir:(Q)=>H(Q,".kode","skills"),detectCommands:["kode"]},mcpjam:{label:"MCPJam",class:"additional",projectSkillsDir:H(".mcpjam","skills"),globalSkillsDir:(Q)=>H(Q,".mcpjam","skills"),detectCommands:["mcpjam"]},"mistral-vibe":{label:"Mistral Vibe",class:"additional",projectSkillsDir:H(".vibe","skills"),globalSkillsDir:(Q)=>H(Q,".vibe","skills"),detectCommands:["mistral-vibe","vibe"]},mux:{label:"Mux",class:"additional",projectSkillsDir:H(".mux","skills"),globalSkillsDir:(Q)=>H(Q,".mux","skills"),detectCommands:["mux"]},neovate:{label:"Neovate",class:"additional",projectSkillsDir:H(".neovate","skills"),globalSkillsDir:(Q)=>H(Q,".neovate","skills"),detectCommands:["neovate"]},opencode:{label:"OpenCode",class:"universal",projectSkillsDir:R,globalSkillsDir:y},openclaw:{label:"OpenClaw",class:"additional",projectSkillsDir:"skills",globalSkillsDir:(Q)=>H(Q,".openclaw","skills"),detectCommands:["openclaw"]},openhands:{label:"OpenHands",class:"additional",projectSkillsDir:H(".openhands","skills"),globalSkillsDir:(Q)=>H(Q,".openhands","skills"),detectCommands:["openhands"]},pi:{label:"Pi",class:"additional",projectSkillsDir:H(".pi","skills"),globalSkillsDir:(Q)=>H(Q,".pi","agent","skills"),detectCommands:["pi"]},pochi:{label:"Pochi",class:"additional",projectSkillsDir:H(".pochi","skills"),globalSkillsDir:(Q)=>H(Q,".pochi","skills"),detectCommands:["pochi"]},qoder:{label:"Qoder",class:"additional",projectSkillsDir:H(".qoder","skills"),globalSkillsDir:(Q)=>H(Q,".qoder","skills"),detectCommands:["qoder"]},"qwen-code":{label:"Qwen Code",class:"additional",projectSkillsDir:H(".qwen","skills"),globalSkillsDir:(Q)=>H(Q,".qwen","skills"),detectCommands:["qwen","qwen-code"]},replit:{label:"Replit",class:"universal",projectSkillsDir:R,globalSkillsDir:y},roo:{label:"Roo Code",class:"additional",projectSkillsDir:H(".roo","skills"),globalSkillsDir:(Q)=>H(Q,".roo","skills"),detectCommands:["roo","roo-code"]},trae:{label:"Trae",class:"additional",projectSkillsDir:H(".trae","skills"),globalSkillsDir:(Q)=>H(Q,".trae","skills"),detectCommands:["trae"]},"trae-cn":{label:"Trae CN",class:"additional",projectSkillsDir:H(".trae","skills"),globalSkillsDir:(Q)=>H(Q,".trae-cn","skills"),detectCommands:["trae-cn","trae"]},windsurf:{label:"Windsurf",class:"additional",projectSkillsDir:H(".windsurf","skills"),globalSkillsDir:(Q)=>H(Q,".codeium","windsurf","skills"),detectCommands:["windsurf"]},zencoder:{label:"Zencoder",class:"additional",projectSkillsDir:H(".zencoder","skills"),globalSkillsDir:(Q)=>H(Q,".zencoder","skills"),detectCommands:["zencoder"]}},t=Object.keys(_),j=Object.fromEntries(t.map((Q)=>[Q,_[Q].label]));function E(){return t.filter((Q)=>_[Q].class==="universal")}function c(){return t.filter((Q)=>_[Q].class==="additional")}function _Q(Q){return _[Q].class==="universal"}async function v(Q){let Z=typeof Q==="string"?{home:Q}:Q??{},X=Z.cwd??process.cwd(),W=Z.commandChecker??EQ,$=[];for(let q of c()){let B=_[q].detectCommands??[],J=!1;for(let z of B)if(await W(z,X)){J=!0;break}if(J)$.push(q)}return $}function g(Q,Z,X){let W=_[Q];if(Z==="project")return H(process.cwd(),W.projectSkillsDir,X);return H(W.globalSkillsDir(WQ()),X)}async function EQ(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 $=IQ(Q,Z,{cwd:X,stdio:"ignore",shell:!1}),q=!1,B=(z)=>{if(q)return;q=!0,W(z)},J=setTimeout(()=>{$.kill("SIGTERM"),B(!1)},1000);$.once("error",()=>{clearTimeout(J),B(!1)}),$.once("exit",(z)=>{clearTimeout(J),B(z===0)})})}class P 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 VQ,mkdir as lQ,rm as GQ,writeFile as iQ}from"fs/promises";import{dirname as sQ,join as FQ}from"path";function $Q(Q){return HQ(Q,[])}function HQ(Q,Z){let X=CQ(Q.meta.name),W=[...Z,X],$=DQ(Q.args),q=NQ(Q.effectiveFlags),B=AQ(Q.subCommands,W);return{name:X,path:W,description:Q.meta.description,usage:Q.meta.usage,runnable:typeof Q.run==="function",args:$,flags:q,children:B}}function CQ(Q){return Q.trim().toLowerCase()}function DQ(Q){if(!Q||Q.length===0)return[];return Q.map(LQ)}function LQ(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=qQ(Q.default);return Z}function NQ(Q){if(!Q)return[];return Object.keys(Q).sort().map((X)=>{return jQ(X,Q[X])})}function jQ(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=qQ(Z.default);return X}function AQ(Q,Z){return Object.keys(Q).sort().map((W)=>{return HQ(Q[W],Z)})}function qQ(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function f(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 BQ(Q){return Q.replace(/(?<!\\)\|/g,"\\|")}function YQ(Q,Z){let X=[],W=zQ(Q);X.push({path:"SKILL.md",content:SQ(Q,Z,W)});for(let $ of W){let q=b($),B=$.children.length>0?pQ($,Q):hQ($,Q);X.push({path:q,content:B})}return X}function zQ(Q){let Z=[Q];for(let X of Q.children)Z.push(...zQ(X));return Z}function b(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function m(Q){return Q.path.join(" ")}function o(Q,Z){let X=Q.split("/").slice(0,-1),W=Z.split("/"),$=0;while($<X.length&&$<W.length&&X[$]===W[$])$++;let q=X.length-$,B=W.slice($);if(q===0)return B.join("/");return[...Array.from({length:q},()=>".."),...B].join("/")}function SQ(Q,Z,X){let W=[];if(W.push("---"),W.push(`name: ${f(Z.name)}`),W.push(`description: ${f(Z.description)}`),Z.license)W.push(`license: ${f(Z.license)}`);if(Z.compatibility)W.push(`compatibility: ${f(Z.compatibility)}`);if(Z.disableModelInvocation)W.push("disable-model-invocation: true");if(Z.allowedTools)W.push(`allowed-tools: ${f(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 $=Z.name.startsWith("use-")?Z.name.slice(4):Z.name;if(W.push(`Use this skill when working with \`${$}\` commands, or when you need help with \`${$}\` 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("4. For any command-specific answer, read that command's documentation file before responding"),W.push("5. Treat the command documentation file as the source of truth for usage, flags, options, aliases, and defaults"),W.push("6. Do not invent or assume undocumented flags/options; if something is missing from the file, say it is not documented"),W.push(""),W.push(...vQ(X)),W.push(""),Q.runnable){W.push("## Usage"),W.push("");let q=b(Q);W.push(`The root command is directly executable. See [${Q.name}](${q}) for usage details.`),W.push("")}return W.join(`
|
|
3
|
-
`)}function
|
|
4
|
-
`)}function
|
|
5
|
-
`)}function
|
|
6
|
-
`}async function
|
|
7
|
-
${
|
|
8
|
-
${
|
|
9
|
-
Skipped ${
|
|
10
|
-
${
|
|
11
|
-
${
|
|
2
|
+
import{accessSync as fQ,constants as hQ}from"fs";import{homedir as e}from"os";import{delimiter as cQ,join as Y}from"path";var y=Y(".agents","skills"),uQ=Y(".crust","skills");function qQ(Q){if(Q!==e())return Y(Q,".config");let X=process.env.XDG_CONFIG_HOME?.trim();return X&&X.length>0?X:Y(Q,".config")}function k(Q){return Y(Q,".agents","skills")}function pQ(Q){return Y(Q,".crust","skills")}var E={amp:{label:"Amp",class:"universal",projectSkillsDir:y,globalSkillsDir:k},adal:{label:"AdaL",class:"additional",projectSkillsDir:Y(".adal","skills"),globalSkillsDir:(Q)=>Y(Q,".adal","skills"),detectCommands:["adal"]},antigravity:{label:"Antigravity",class:"additional",projectSkillsDir:Y(".agent","skills"),globalSkillsDir:(Q)=>Y(Q,".gemini","antigravity","skills"),detectCommands:["antigravity"]},augment:{label:"Augment",class:"additional",projectSkillsDir:Y(".augment","skills"),globalSkillsDir:(Q)=>Y(Q,".augment","skills"),detectCommands:["augment"]},"claude-code":{label:"Claude Code",class:"additional",projectSkillsDir:Y(".claude","skills"),globalSkillsDir:(Q)=>Y(process.env.CLAUDE_CONFIG_DIR?.trim()||Y(Q,".claude"),"skills"),detectCommands:["claude","claude-code"]},cline:{label:"Cline",class:"universal",projectSkillsDir:y,globalSkillsDir:k},codebuddy:{label:"CodeBuddy",class:"additional",projectSkillsDir:Y(".codebuddy","skills"),globalSkillsDir:(Q)=>Y(Q,".codebuddy","skills"),detectCommands:["codebuddy"]},codex:{label:"Codex",class:"universal",projectSkillsDir:y,globalSkillsDir:k},"command-code":{label:"Command Code",class:"additional",projectSkillsDir:Y(".commandcode","skills"),globalSkillsDir:(Q)=>Y(Q,".commandcode","skills"),detectCommands:["command-code","commandcode"]},continue:{label:"Continue",class:"additional",projectSkillsDir:Y(".continue","skills"),globalSkillsDir:(Q)=>Y(Q,".continue","skills"),detectCommands:["continue"]},cortex:{label:"Cortex Code",class:"additional",projectSkillsDir:Y(".cortex","skills"),globalSkillsDir:(Q)=>Y(Q,".snowflake","cortex","skills"),detectCommands:["cortex"]},crush:{label:"Crush",class:"additional",projectSkillsDir:Y(".crush","skills"),globalSkillsDir:(Q)=>Y(qQ(Q),"crush","skills"),detectCommands:["crush"]},cursor:{label:"Cursor",class:"universal",projectSkillsDir:y,globalSkillsDir:k},droid:{label:"Droid",class:"additional",projectSkillsDir:Y(".factory","skills"),globalSkillsDir:(Q)=>Y(Q,".factory","skills"),detectCommands:["droid"]},"gemini-cli":{label:"Gemini CLI",class:"universal",projectSkillsDir:y,globalSkillsDir:k},"github-copilot":{label:"GitHub Copilot",class:"universal",projectSkillsDir:y,globalSkillsDir:k},goose:{label:"Goose",class:"additional",projectSkillsDir:Y(".goose","skills"),globalSkillsDir:(Q)=>Y(qQ(Q),"goose","skills"),detectCommands:["goose"]},"iflow-cli":{label:"iFlow CLI",class:"additional",projectSkillsDir:Y(".iflow","skills"),globalSkillsDir:(Q)=>Y(Q,".iflow","skills"),detectCommands:["iflow","iflow-cli"]},junie:{label:"Junie",class:"additional",projectSkillsDir:Y(".junie","skills"),globalSkillsDir:(Q)=>Y(Q,".junie","skills"),detectCommands:["junie"]},kilo:{label:"Kilo Code",class:"additional",projectSkillsDir:Y(".kilocode","skills"),globalSkillsDir:(Q)=>Y(Q,".kilocode","skills"),detectCommands:["kilo","kilocode"]},"kimi-cli":{label:"Kimi Code CLI",class:"universal",projectSkillsDir:y,globalSkillsDir:k},"kiro-cli":{label:"Kiro CLI",class:"additional",projectSkillsDir:Y(".kiro","skills"),globalSkillsDir:(Q)=>Y(Q,".kiro","skills"),detectCommands:["kiro","kiro-cli"]},kode:{label:"Kode",class:"additional",projectSkillsDir:Y(".kode","skills"),globalSkillsDir:(Q)=>Y(Q,".kode","skills"),detectCommands:["kode"]},mcpjam:{label:"MCPJam",class:"additional",projectSkillsDir:Y(".mcpjam","skills"),globalSkillsDir:(Q)=>Y(Q,".mcpjam","skills"),detectCommands:["mcpjam"]},"mistral-vibe":{label:"Mistral Vibe",class:"additional",projectSkillsDir:Y(".vibe","skills"),globalSkillsDir:(Q)=>Y(Q,".vibe","skills"),detectCommands:["mistral-vibe","vibe"]},mux:{label:"Mux",class:"additional",projectSkillsDir:Y(".mux","skills"),globalSkillsDir:(Q)=>Y(Q,".mux","skills"),detectCommands:["mux"]},neovate:{label:"Neovate",class:"additional",projectSkillsDir:Y(".neovate","skills"),globalSkillsDir:(Q)=>Y(Q,".neovate","skills"),detectCommands:["neovate"]},opencode:{label:"OpenCode",class:"universal",projectSkillsDir:y,globalSkillsDir:k},openclaw:{label:"OpenClaw",class:"additional",projectSkillsDir:"skills",globalSkillsDir:(Q)=>Y(Q,".openclaw","skills"),detectCommands:["openclaw"]},openhands:{label:"OpenHands",class:"additional",projectSkillsDir:Y(".openhands","skills"),globalSkillsDir:(Q)=>Y(Q,".openhands","skills"),detectCommands:["openhands"]},pi:{label:"Pi",class:"additional",projectSkillsDir:Y(".pi","skills"),globalSkillsDir:(Q)=>Y(Q,".pi","agent","skills"),detectCommands:["pi"]},pochi:{label:"Pochi",class:"additional",projectSkillsDir:Y(".pochi","skills"),globalSkillsDir:(Q)=>Y(Q,".pochi","skills"),detectCommands:["pochi"]},qoder:{label:"Qoder",class:"additional",projectSkillsDir:Y(".qoder","skills"),globalSkillsDir:(Q)=>Y(Q,".qoder","skills"),detectCommands:["qoder"]},"qwen-code":{label:"Qwen Code",class:"additional",projectSkillsDir:Y(".qwen","skills"),globalSkillsDir:(Q)=>Y(Q,".qwen","skills"),detectCommands:["qwen","qwen-code"]},replit:{label:"Replit",class:"universal",projectSkillsDir:y,globalSkillsDir:k},roo:{label:"Roo Code",class:"additional",projectSkillsDir:Y(".roo","skills"),globalSkillsDir:(Q)=>Y(Q,".roo","skills"),detectCommands:["roo","roo-code"]},trae:{label:"Trae",class:"additional",projectSkillsDir:Y(".trae","skills"),globalSkillsDir:(Q)=>Y(Q,".trae","skills"),detectCommands:["trae"]},"trae-cn":{label:"Trae CN",class:"additional",projectSkillsDir:Y(".trae","skills"),globalSkillsDir:(Q)=>Y(Q,".trae-cn","skills"),detectCommands:["trae-cn","trae"]},windsurf:{label:"Windsurf",class:"additional",projectSkillsDir:Y(".windsurf","skills"),globalSkillsDir:(Q)=>Y(Q,".codeium","windsurf","skills"),detectCommands:["windsurf"]},zencoder:{label:"Zencoder",class:"additional",projectSkillsDir:Y(".zencoder","skills"),globalSkillsDir:(Q)=>Y(Q,".zencoder","skills"),detectCommands:["zencoder"]}},p=Object.keys(E),P=Object.fromEntries(p.map((Q)=>[Q,E[Q].label]));function C(){return p.filter((Q)=>E[Q].class==="universal")}function D(){return p.filter((Q)=>E[Q].class==="additional")}function gQ(Q){return E[Q].class==="universal"}async function QQ(Q){let X=typeof Q==="string"?{home:Q}:Q??{},Z=X.cwd??process.cwd(),$=X.commandChecker??((W)=>Promise.resolve(mQ(W))),H=[];for(let W of D()){let z=E[W].detectCommands??[],J=!1;for(let q of z)if(await $(q,Z)){J=!0;break}if(J)H.push(W)}return H}function v(Q,X,Z){let $=E[Q];if(X==="project")return Y(process.cwd(),$.projectSkillsDir,Z);return Y($.globalSkillsDir(e()),Z)}function s(Q,X){if(Q==="project")return Y(process.cwd(),uQ,X);return Y(pQ(e()),X)}function mQ(Q){let Z=(process.env.PATH??"").split(cQ).filter((W)=>W.length>0),$=process.platform==="win32",H=$?(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";").filter((W)=>W.length>0):[];for(let W of Z){if(!$&&zQ(Y(W,Q)))return!0;if($){for(let z of H)if(zQ(Y(W,Q+z)))return!0}}return!1}function zQ(Q){try{return fQ(Q,hQ.X_OK),!0}catch{return!1}}class b extends Error{name="SkillConflictError";details;constructor(Q){let X=`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(X);this.details=Q}}import{lstat as bQ,mkdir as IQ,readlink as BZ,realpath as qZ,rm as $Q,symlink as zZ,writeFile as JZ}from"fs/promises";import{dirname as EQ,join as _Q}from"path";function JQ(Q){return KQ(Q,[])}function KQ(Q,X){let Z=dQ(Q.meta.name),$=[...X,Z],H=rQ(Q.args),W=sQ(Q.effectiveFlags),z=oQ(Q.subCommands,$);return{name:Z,path:$,description:Q.meta.description,usage:Q.meta.usage,runnable:typeof Q.run==="function",args:H,flags:W,children:z}}function dQ(Q){return Q.trim().toLowerCase()}function rQ(Q){if(!Q||Q.length===0)return[];return Q.map(lQ)}function lQ(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=xQ(Q.default);return X}function sQ(Q){if(!Q)return[];return Object.keys(Q).sort().map((Z)=>{return iQ(Z,Q[Z])})}function iQ(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=xQ(X.default);return Z}function oQ(Q,X){return Object.keys(Q).sort().map(($)=>{return KQ(Q[$],X)})}function xQ(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function g(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 MQ(Q){return Q.replace(/(?<!\\)\|/g,"\\|")}function GQ(Q,X){let Z=[],$=wQ(Q);Z.push({path:"SKILL.md",content:tQ(Q,X,$)});for(let H of $){let W=L(H),z=H.children.length>0?QZ(H,Q):eQ(H,Q);Z.push({path:W,content:z})}return Z}function wQ(Q){let X=[Q];for(let Z of Q.children)X.push(...wQ(Z));return X}function L(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function i(Q){return Q.path.join(" ")}function ZQ(Q,X){let Z=Q.split("/").slice(0,-1),$=X.split("/"),H=0;while(H<Z.length&&H<$.length&&Z[H]===$[H])H++;let W=Z.length-H,z=$.slice(H);if(W===0)return z.join("/");return[...Array.from({length:W},()=>".."),...z].join("/")}function tQ(Q,X,Z){let $=[];if($.push("---"),$.push(`name: ${g(X.name)}`),$.push(`description: ${g(X.description)}`),X.license)$.push(`license: ${g(X.license)}`);if(X.compatibility)$.push(`compatibility: ${g(X.compatibility)}`);if(X.disableModelInvocation)$.push("disable-model-invocation: true");if(X.allowedTools)$.push(`allowed-tools: ${g(X.allowedTools)}`);if($.push("metadata:"),$.push(` version: "${X.version}"`),$.push("---"),$.push(""),$.push(`# ${X.name}`),$.push(""),Q.description)$.push(Q.description),$.push("");let H=X.name.startsWith("use-")?X.name.slice(4):X.name;if($.push(`Use this skill when working with \`${H}\` commands, or when you need help with \`${H}\` syntax, flags, or subcommands.`),$.push(""),$.push("## Command Reference"),$.push(""),$.push("This table lists all commands and their documentation paths. **Do not read all command files at once.** Instead:"),$.push(""),$.push("1. Use the table below to find the relevant command"),$.push("2. Use the `Type` column to choose what to execute: commands labeled `runnable` (including `runnable, group`) are executable, while `group` commands are not"),$.push("3. Read only the specific file from the `commands/` directory that you need"),$.push("4. For any command-specific answer, read that command's documentation file before responding"),$.push("5. Treat the command documentation file as the source of truth for usage, flags, options, aliases, and defaults"),$.push("6. Do not invent or assume undocumented flags/options; if something is missing from the file, say it is not documented"),$.push(""),$.push(...aQ(Z)),$.push(""),Q.runnable){$.push("## Usage"),$.push("");let W=L(Q);$.push(`The root command is directly executable. See [${Q.name}](${W}) for usage details.`),$.push("")}return $.join(`
|
|
3
|
+
`)}function aQ(Q){let X=[];X.push("| Command | Type | Documentation |"),X.push("| ------- | ---- | ------------- |");for(let Z of Q){let $=i(Z),H=L(Z),W=nQ(Z);X.push(`| \`${$}\` | ${W} | [${H}](${H}) |`)}return X}function nQ(Q){if(Q.runnable&&Q.children.length>0)return"runnable, group";if(Q.runnable)return"runnable";return"group"}function eQ(Q,X){let Z=[],$=i(Q);if(Z.push(`# \`${$}\``),Z.push(""),Q.description)Z.push(Q.description),Z.push("");if(Z.push("## Usage"),Z.push(""),Q.usage)Z.push("```"),Z.push(Q.usage),Z.push("```");else Z.push("```"),Z.push(FQ(Q)),Z.push("```");if(Z.push(""),Q.args.length>0)Z.push("## Arguments"),Z.push(""),Z.push(...OQ(Q.args)),Z.push("");if(Q.flags.length>0)Z.push("## Flags"),Z.push(""),Z.push(...UQ(Q.flags)),Z.push("");return Z.push("## Command Documentation Authority"),Z.push(""),Z.push("Only arguments, flags, options, aliases, and defaults documented in this file are supported for this command."),Z.push("Do not infer or invent additional command-line options."),Z.push(""),Z.push(...VQ(Q,X)),Z.join(`
|
|
4
|
+
`)}function QZ(Q,X){let Z=[],$=i(Q),H=L(Q);if(Z.push(`# \`${$}\``),Z.push(""),Q.description)Z.push(Q.description),Z.push("");if(Q.runnable){if(Z.push("## Usage"),Z.push(""),Q.usage)Z.push("```"),Z.push(Q.usage),Z.push("```");else Z.push("```"),Z.push(FQ(Q)),Z.push("```");if(Z.push(""),Q.args.length>0)Z.push("## Arguments"),Z.push(""),Z.push(...OQ(Q.args)),Z.push("");if(Q.flags.length>0)Z.push("## Flags"),Z.push(""),Z.push(...UQ(Q.flags)),Z.push("");Z.push("## Command Documentation Authority"),Z.push(""),Z.push("Only arguments, flags, options, aliases, and defaults documented in this file are supported for this command."),Z.push("Do not infer or invent additional command-line options."),Z.push("")}Z.push("## Subcommands"),Z.push("");for(let W of Q.children){let z=L(W),J=ZQ(H,z),q=W.description?` - ${W.description}`:"";Z.push(`- [\`${W.name}\`](${J})${q}`)}return Z.push(""),Z.push(...VQ(Q,X)),Z.join(`
|
|
5
|
+
`)}function FQ(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 OQ(Q){let X=[];X.push("| Argument | Type | Required | Description |"),X.push("| -------- | ---- | -------- | ----------- |");for(let Z of Q){let $=Z.variadic?`${Z.name}...`:Z.name,H=Z.required?"Yes":"No",W=MQ(ZZ(Z));X.push(`| \`${$}\` | ${Z.type} | ${H} | ${W} |`)}return X}function ZZ(Q){let X=[];if(Q.description)X.push(Q.description);if(Q.default!==void 0)X.push(`Default: \`${Q.default}\``);return X.join(". ")||"-"}function UQ(Q){let X=[];X.push("| Flag | Type | Required | Description |"),X.push("| ---- | ---- | -------- | ----------- |");for(let Z of Q){let $=XZ(Z),H=Z.required?"Yes":"No",W=MQ($Z(Z));X.push(`| ${$} | ${Z.type} | ${H} | ${W} |`)}return X}function XZ(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 $Z(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 VQ(Q,X){let Z=[],$=L(Q);if(Z.push("---"),Z.push(""),Q.path.length>1){let W=Q.path.slice(0,-1),z=RQ(X,W);if(z){let J=L(z),q=ZQ($,J),K=i(z);Z.push(`Parent: [\`${K}\`](${q})`),Z.push("")}}let H=ZQ($,"SKILL.md");return Z.push(`[Skill Overview](${H})`),Z.push(""),Z}function RQ(Q,X){if(HZ(Q.path,X))return Q;for(let Z of Q.children){let $=RQ(Z,X);if($)return $}return}function HZ(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 WZ}from"fs/promises";import{join as YZ}from"path";var XQ="crust.json";async function m(Q){try{let X=await WZ(YZ(Q,XQ),"utf-8"),Z=JSON.parse(X);if(typeof Z==="object"&&Z!==null&&"version"in Z&&typeof Z.version==="string")return Z.version;return null}catch{return null}}var KZ="auto",PQ=/^[a-z0-9]+(-[a-z0-9]+)*$/;function CQ(Q){return Q.length>=1&&Q.length<=64&&PQ.test(Q)}function S(Q){return Q.startsWith("use-")?Q:`use-${Q}`}async function A(Q){let{command:X,meta:Z,agents:$,scope:H="global",clean:W=!0,force:z=!1,installMode:J=KZ}=Q,q=S(Z.name);if(!CQ(q))throw Error(`Invalid skill name "${q}": must be 1\u201364 lowercase `+`alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens. Pattern: ${PQ.source}`);let K=$[0];if(!K)return{agents:[]};let x={...Z,name:q},G=JQ(X),w=GQ(G,x),h=OZ(x),N=[...w,...h].sort((M,V)=>M.path<V.path?-1:M.path>V.path?1:0),a=N.map((M)=>M.path),R=new Map;for(let M of $){let V=v(M,H,x.name),T=R.get(V);if(T)T.push(M);else R.set(V,[M])}let l=new Map;for(let M of R.keys())l.set(M,await m(M));let _=s(H,x.name),c=await m(_);if(c===null){if(await NQ(_)&&!z)throw new b({agent:K,outputDir:v(K,H,x.name)})}let u=c!==x.version;if(u){if(W)await WQ(_);await jQ(_,N)}let j=[];for(let[M,V]of R){let T=V[0];if(!T)continue;let I=l.get(M)??null,B=await LQ(M,_),F=I!==null||B.exists&&B.isSymlink&&B.pointsToCanonical;if(B.exists&&!F&&!z)throw new b({agent:T,outputDir:M});let O=await MZ({outputDir:M,canonicalOutputDir:_,allFiles:N,clean:W,installMode:J,inspection:B,installedVersion:I,currentVersion:x.version}),U=xZ({installedVersion:I,currentVersion:x.version,canonicalChanged:u,pathChanged:O});for(let n of V)j.push({agent:n,outputDir:M,files:U==="up-to-date"?[]:a,status:U,previousVersion:U==="updated"?I??void 0:void 0})}return{agents:j}}async function HQ(Q){let{name:X,agents:Z,scope:$="global"}=Q,H=S(X),W=s($,H),z=[],J=new Map;for(let K of Z){let x=v(K,$,H),G=J.get(x);if(G)G.push(K);else J.set(x,[K])}for(let[K,x]of J)if(await NQ(K)){await $Q(K,{recursive:!0,force:!0});for(let w of x)z.push({agent:w,outputDir:K,status:"removed"})}else for(let w of x)z.push({agent:w,outputDir:K,status:"not-found"});if(!await FZ(H,$))await $Q(W,{recursive:!0,force:!0});return{agents:z}}async function d(Q){let{name:X,agents:Z,scope:$="global"}=Q,H=S(X),W=[],z=new Map;for(let J of Z){let q=v(J,$,H),K=z.get(q);if(K)K.push(J);else z.set(q,[J])}for(let[J,q]of z){let K=await m(J);for(let x of q)W.push({agent:x,outputDir:J,installed:K!==null,version:K??void 0})}return{agents:W}}function xZ(Q){let{installedVersion:X,currentVersion:Z,canonicalChanged:$,pathChanged:H}=Q;if(X===null)return"installed";if(X===Z&&!$&&!H)return"up-to-date";return"updated"}async function MZ(Q){let{outputDir:X,canonicalOutputDir:Z,allFiles:$,clean:H,installMode:W,inspection:z,installedVersion:J,currentVersion:q}=Q;if(W==="copy")return TQ({outputDir:X,allFiles:$,clean:H,inspection:z,installedVersion:J,currentVersion:q});try{return await GZ({outputDir:X,canonicalOutputDir:Z,inspection:z})}catch(K){if(W==="symlink")throw Error(`Failed to create symlink at "${X}" (installMode: symlink).`,{cause:K});let x=await LQ(X,Z);return TQ({outputDir:X,allFiles:$,clean:H,inspection:x,installedVersion:J,currentVersion:q})}}async function TQ(Q){let{outputDir:X,allFiles:Z,clean:$,inspection:H,installedVersion:W,currentVersion:z}=Q;if(!(!H.exists||H.isSymlink||W!==z))return!1;if(H.isSymlink||$)await WQ(X);return await jQ(X,Z),!0}async function GZ(Q){let{outputDir:X,canonicalOutputDir:Z,inspection:$}=Q;if($.exists&&$.isSymlink&&$.pointsToCanonical)return!1;if($.exists)await WQ(X);return await wZ(Z,X),!0}async function LQ(Q,X){let Z;try{Z=await bQ(Q)}catch{return{exists:!1,isSymlink:!1,pointsToCanonical:!1}}let $=process.platform==="win32"&&Z.isDirectory()&&await kQ(Q)!==null;if(!(Z.isSymbolicLink()||$))return{exists:!0,isSymlink:!1,pointsToCanonical:!1};let[W,z,J]=await Promise.all([yQ(Q),yQ(X),kQ(Q)]);return{exists:!0,isSymlink:!0,pointsToCanonical:W!==null&&z!==null&&W===z||J===X}}async function yQ(Q){try{return await qZ(Q)}catch{return null}}async function kQ(Q){try{return await BZ(Q)}catch{return null}}async function wZ(Q,X){await IQ(EQ(X),{recursive:!0});let Z=process.platform==="win32"?"junction":"dir";await zZ(Q,X,Z)}async function NQ(Q){try{return await bQ(Q),!0}catch{return!1}}async function FZ(Q,X){let Z=new Set;for(let $ of p)Z.add(v($,X,Q));for(let $ of Z)if(await m($)!==null)return!0;return!1}function OZ(Q){let X={name:Q.name,description:Q.description,version:Q.version};return[{path:XQ,content:`${JSON.stringify(X,null,"\t")}
|
|
6
|
+
`}]}async function WQ(Q){await $Q(Q,{recursive:!0,force:!0})}async function jQ(Q,X){let Z=new Set;for(let H of X){let W=_Q(Q,H.path),z=EQ(W);Z.add(z)}let $=[...Z].sort();for(let H of $)await IQ(H,{recursive:!0});for(let H of X){let W=_Q(Q,H.path);await JZ(W,H.content,"utf-8")}}import{Crust as vQ,VALIDATION_MODE_ENV as UZ}from"@crustjs/core";import{confirm as VZ,multiselect as RZ,select as _Z,spinner as r}from"@crustjs/prompts";import{bold as t,dim as f,yellow as SQ}from"@crustjs/style";var TZ="skill",yZ="global",o="__universal__";function kZ(Q){return Q==="global"||Q==="project"}async function AQ(Q,X){if(Q!==void 0){if(!kZ(Q))throw Error(`Invalid --scope value: ${String(Q)}. Expected "project" or "global".`);return Q}if(X.defaultScope)return X.defaultScope;return _Z({message:"Select scope",choices:[{label:"Project",value:"project"},{label:"Global",value:"global"}],default:yZ})}function YQ(Q){let X=new Set(C()),Z=[];if(Q.some(($)=>X.has($)))Z.push("Universal");for(let $ of Q){if(X.has($))continue;Z.push(P[$])}return Z}function DQ(Q){let X=new Set(C()),Z=[],$=Q.find((H)=>X.has(H.agent));if($)Z.push({label:"Universal",outputDir:$.outputDir});for(let H of Q){if(X.has(H.agent))continue;Z.push({label:P[H.agent],outputDir:H.outputDir})}return Z}function BQ(Q,X){return{name:Q.meta.name,description:Q.meta.description??"",version:X}}async function bZ(Q,X){let Z=[...C(),...D()];if(Z.length===0)return;let $=BQ(Q,X.version),H=["project","global"];for(let W of H){let J=(await d({name:$.name,agents:Z,scope:W})).agents.filter((q)=>q.installed&&q.version!==$.version);if(J.length===0)continue;try{await r({message:`Updating ${W} skills...`,task:async({updateMessage:q})=>{let K=await A({command:Q,meta:$,agents:J.map((w)=>w.agent),scope:W,installMode:X.installMode}),x=K.agents.filter((w)=>w.status==="updated").map((w)=>w.agent),G=YQ(x);if(G.length>0)q(`Updated skill "${S($.name)}" to v${$.version} for ${G.join(", ")} (${W})`);return K}})}catch(q){if(q instanceof b)console.warn(SQ(`Skill conflict: "${q.details.outputDir}" already exists but was not created by ${$.name}. Skipping auto-update for ${W}. Delete or rename the conflicting skill to resolve.`));else throw q}}}function IZ(Q){let X;return{name:"skills",async setup(Z,$){X=Z.rootCommand;let H=Q.command??TZ;if($.addSubCommand(X,H,EZ(X,Q,H)),process.env[UZ]==="1")return;if(Z.argv[0]===H)return;if(Q.autoUpdate!==!1)await bZ(X,Q)}}}function EZ(Q,X,Z){let $=PZ(Q,X);return new vQ(Z).meta({description:"Manage agent skill installations"}).flags({scope:{type:"string",description:"Install scope (project or global)"}}).run(async(H)=>{let W=BQ(Q,X.version),z=await AQ(H.flags.scope,X),J=await QQ(),q=C(),K=D(),x=await d({name:W.name,agents:[...q,...K],scope:z}),G=new Set(x.agents.filter((B)=>B.installed).map((B)=>B.agent)),w=new Set(J),h=new Map(x.agents.map((B)=>[B.agent,B])),N=K.filter((B)=>{if(w.has(B))return!0;return h.get(B)?.installed===!0}),a=N.filter((B)=>G.has(B)),R=[];if(q.length>0){let B=q[0];if(!B)throw Error("Expected at least one universal agent");let O=h.get(B)?.outputDir??"path unavailable";R.push({label:"Universal",value:o,hint:O});let U=q.map((n)=>P[n]).join(", ");console.log(f(`Agents supporting universal skills: ${U}`))}for(let B of N){let O=h.get(B)?.outputDir??"path unavailable";R.push({label:P[B],value:B,hint:O})}let l=q.length>0&&q.every((B)=>G.has(B)),_=[...a.filter((B)=>!q.includes(B))];if(l)_.unshift(o);let c=R.length===0?[]:await RZ({message:"Select agents to install skills for",choices:R,default:_,required:!1}),u=new Set(c.filter((B)=>B!==o));if(c.includes(o))for(let B of q)u.add(B);let j=[...u],M=j.filter((B)=>!G.has(B)),V=j.filter((B)=>{let F=x.agents.find((O)=>O.agent===B);return F?.installed===!0&&F.version!==W.version}),T=[...G].filter((B)=>!j.includes(B)),I=[...M,...V];if(I.length>0)try{let B=await r({message:"Installing skills...",task:async()=>A({command:Q,meta:W,agents:I,scope:z,installMode:X.installMode})});console.log(`
|
|
7
|
+
${t(`Installed "${W.name}" v${W.version}`)}`);for(let F of DQ(B.agents))console.log(f(` ${F.label} \u2192 ${F.outputDir}`))}catch(B){if(B instanceof b)if(await VZ({message:`"${B.details.outputDir}" already exists but was not created by Crust. Overwrite?`,default:!1})){let O=await r({message:"Overwriting skill...",task:async()=>A({command:Q,meta:W,agents:[B.details.agent],scope:z,force:!0,installMode:X.installMode})});console.log(`
|
|
8
|
+
${t(`Installed "${W.name}" v${W.version}`)}`);for(let U of DQ(O.agents))console.log(f(` ${U.label} \u2192 ${U.outputDir}`))}else console.log(f(`
|
|
9
|
+
Skipped ${P[B.details.agent]}`));else throw B}if(T.length>0){let F=(await r({message:"Removing skills...",task:async()=>HQ({name:W.name,agents:T,scope:z})})).agents.filter((U)=>U.status==="removed").map((U)=>U.agent),O=YQ(F);if(O.length>0)console.log(`
|
|
10
|
+
${t(`Removed from ${O.join(", ")}`)}`)}if(I.length===0&&T.length===0)console.log(f("No changes."))}).command($)._node}function PZ(Q,X){return new vQ("update").meta({description:"Update installed skills to latest version"}).flags({scope:{type:"string",description:"Update scope (project or global)"}}).run(async(Z)=>{let $=await AQ(Z.flags.scope,X),H=[...C(),...D()],W=BQ(Q,X.version),J=(await d({name:W.name,agents:H,scope:$})).agents.filter((q)=>q.installed&&q.version!==W.version);if(J.length===0){console.log(f(`No updates needed (${$}).`));return}try{let K=(await r({message:`Updating ${$} skills...`,task:async()=>A({command:Q,meta:W,agents:J.map((G)=>G.agent),scope:$,installMode:X.installMode})})).agents.filter((G)=>G.status==="updated").map((G)=>G.agent),x=YQ(K);if(x.length>0)console.log(`
|
|
11
|
+
${t(`Updated "${W.name}" to v${W.version} for ${x.join(", ")} (${$})`)}`)}catch(q){if(q instanceof b)console.warn(SQ(`Skipped ${P[q.details.agent]}: "${q.details.outputDir}" already exists but was not created by ${W.name}. Delete or rename the conflicting directory to resolve.`));else throw q}})}export{HQ as uninstallSkill,d as skillStatus,IZ as skillPlugin,S as resolveSkillName,s as resolveCanonicalSkillPath,CQ as isValidSkillName,gQ as isUniversalAgent,C as getUniversalAgents,D as getAdditionalAgents,A as generateSkill,QQ as detectInstalledAgents,b as SkillConflictError};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crustjs/skills",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "Agent skill generation from Crust command definitions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,16 +42,16 @@
|
|
|
42
42
|
"test": "bun test"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@crustjs/prompts": "0.0.
|
|
46
|
-
"@crustjs/style": "0.0.
|
|
45
|
+
"@crustjs/prompts": "0.0.9",
|
|
46
|
+
"@crustjs/style": "0.0.5"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@crustjs/config": "0.0.0",
|
|
50
|
-
"@crustjs/core": "0.0.
|
|
50
|
+
"@crustjs/core": "0.0.13",
|
|
51
51
|
"bunup": "^0.16.29"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@crustjs/core": "0.0.
|
|
54
|
+
"@crustjs/core": "0.0.13",
|
|
55
55
|
"typescript": "^5"
|
|
56
56
|
}
|
|
57
57
|
}
|