@crustjs/skills 0.0.16 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,6 +59,13 @@ runMain(app, {
59
59
  plugins: [
60
60
  skillPlugin({
61
61
  version: "1.0.0",
62
+ instructions: `
63
+ Prefer readonly commands before mutating project state.
64
+
65
+ ## Response Policy
66
+
67
+ - Read the relevant command doc before suggesting flags.
68
+ `,
62
69
  // autoUpdate: true (default) — silently updates installed skills
63
70
  // command: "skill" (default) — registers "my-cli skill" subcommand
64
71
  // defaultScope: "global" | "project" — skip scope prompt when set
@@ -135,6 +142,53 @@ if (import.meta.main) {
135
142
  }
136
143
  ```
137
144
 
145
+ ### Custom Instructions
146
+
147
+ Use plugin-level `instructions` to add top-level guidance to the generated
148
+ `SKILL.md`, and `annotate()` to add prompt guidance to specific
149
+ command docs under `commands/`.
150
+
151
+ - `instructions: string` renders as a raw markdown block.
152
+ - `instructions: string[]` renders as bullet list items.
153
+ - Empty or whitespace-only instruction input is ignored.
154
+ - `annotate()` always renders command guidance as bullets.
155
+
156
+ ```ts
157
+ import { Crust } from "@crustjs/core";
158
+ import { annotate, skillPlugin } from "@crustjs/skills";
159
+
160
+ const deploy = annotate(
161
+ new Crust("deploy")
162
+ .meta({ description: "Deploy the application" })
163
+ .flags({
164
+ "dry-run": { type: "boolean", description: "Preview changes only" },
165
+ })
166
+ .run(() => {
167
+ // ...
168
+ }),
169
+ [
170
+ "Prefer `--dry-run` before executing deployment changes.",
171
+ "Ask for confirmation before production deployments.",
172
+ ],
173
+ );
174
+
175
+ const app = new Crust("my-cli")
176
+ .meta({ description: "My CLI" })
177
+ .use(
178
+ skillPlugin({
179
+ version: "1.0.0",
180
+ instructions: `
181
+ Read command docs before suggesting exact flags.
182
+
183
+ ## Answer Style
184
+
185
+ - Prefer exact syntax copied from the relevant command file.
186
+ `,
187
+ }),
188
+ )
189
+ .command(deploy);
190
+ ```
191
+
138
192
  This pattern lets `crust skills generate` import the command definition without triggering `runMain`.
139
193
 
140
194
  ## CLI Usage
@@ -191,7 +245,12 @@ import { generateSkill } from "@crustjs/skills";
191
245
 
192
246
  const result = await generateSkill({
193
247
  command: rootCommand,
194
- meta: { name: "my-cli", description: "My CLI tool", version: "1.0.0" },
248
+ meta: {
249
+ name: "my-cli",
250
+ description: "My CLI tool",
251
+ version: "1.0.0",
252
+ instructions: ["Prefer readonly commands before making changes."],
253
+ },
195
254
  agents: ["opencode"],
196
255
  scope: "project", // default: "global"
197
256
  installMode: "auto", // default: "auto" — symlink first, fallback to copy
@@ -236,11 +295,11 @@ Resolves the canonical store path where Crust writes the single source-of-truth
236
295
  ```ts
237
296
  import { resolveCanonicalSkillPath } from "@crustjs/skills";
238
297
 
239
- resolveCanonicalSkillPath("project", "use-my-cli");
240
- // → "<cwd>/.crust/skills/use-my-cli"
298
+ resolveCanonicalSkillPath("project", "my-cli");
299
+ // → "<cwd>/.crust/skills/my-cli"
241
300
 
242
- resolveCanonicalSkillPath("global", "use-my-cli");
243
- // → "~/.crust/skills/use-my-cli"
301
+ resolveCanonicalSkillPath("global", "my-cli");
302
+ // → "~/.crust/skills/my-cli"
244
303
  ```
245
304
 
246
305
  ### `isValidSkillName(name)`
@@ -256,7 +315,7 @@ isValidSkillName("-leading"); // false — leading hyphen
256
315
  isValidSkillName("a".repeat(65)); // false — exceeds 64 characters
257
316
  ```
258
317
 
259
- > **Note:** `generateSkill()` automatically validates `meta.name` (after prepending `use-`) and throws a descriptive error if the name is invalid.
318
+ > **Note:** `generateSkill()` automatically validates `meta.name` and throws a descriptive error if the name is invalid.
260
319
 
261
320
  ## Skill Metadata
262
321
 
@@ -294,10 +353,10 @@ No manual escaping is needed — pass raw values and the renderer handles the re
294
353
 
295
354
  ## Output Structure
296
355
 
297
- Generated output goes to `<outDir>/skills/use-<name>/` (the `use-` prefix is applied automatically):
356
+ Generated output goes to `<outDir>/skills/<name>/`:
298
357
 
299
358
  ```
300
- skills/use-my-cli/
359
+ skills/my-cli/
301
360
  SKILL.md # Entrypoint — loaded by the agent
302
361
  commands/ # Per-command documentation mirroring the CLI hierarchy
303
362
  my-cli.md # Root command
@@ -346,19 +405,19 @@ After generating a skill bundle, consumers can install it by copying the skill d
346
405
  ### Universal agents (OpenCode, Codex, Cursor, and others)
347
406
 
348
407
  ```sh
349
- cp -r skills/use-my-cli/ .agents/skills/use-my-cli/
408
+ cp -r skills/my-cli/ .agents/skills/my-cli/
350
409
  ```
351
410
 
352
411
  Global install for universal agents:
353
412
 
354
413
  ```sh
355
- cp -r skills/use-my-cli/ ~/.agents/skills/use-my-cli/
414
+ cp -r skills/my-cli/ ~/.agents/skills/my-cli/
356
415
  ```
357
416
 
358
417
  ### Claude Code
359
418
 
360
419
  ```sh
361
- cp -r skills/use-my-cli/ .claude/skills/use-my-cli/
420
+ cp -r skills/my-cli/ .claude/skills/my-cli/
362
421
  ```
363
422
 
364
423
  The agent will discover the skill from `SKILL.md` and load command documentation on demand from the `commands/` directory.
package/dist/index.d.ts CHANGED
@@ -12,21 +12,21 @@ import { CommandNode } from "@crustjs/core";
12
12
  * description: "CLI tool for managing widgets",
13
13
  * version: "1.0.0",
14
14
  * };
15
- * // generateSkill() will output to `use-my-cli/` with name "use-my-cli"
15
+ * // generateSkill() will output to `my-cli/` with name "my-cli"
16
16
  * ```
17
17
  */
18
18
  interface SkillMeta {
19
19
  /**
20
20
  * Skill name — the user-facing CLI name (e.g. `"my-cli"`).
21
21
  *
22
- * `generateSkill()`, `uninstallSkill()`, and `skillStatus()` automatically
23
- * prefix `use-` to this name for output directory paths, SKILL.md frontmatter,
24
- * and crust.json metadata. For example, `name: "my-cli"` produces output
25
- * under `use-my-cli/`.
22
+ * `generateSkill()`, `uninstallSkill()`, and `skillStatus()` treat this as
23
+ * the canonical raw skill name for output directory paths, SKILL.md
24
+ * frontmatter, and crust.json metadata. For example, `name: "my-cli"`
25
+ * produces output under `my-cli/`.
26
26
  *
27
- * The resolved name (with `use-` prefix) must conform to the Agent Skills
28
- * spec: 1–64 lowercase alphanumeric characters and hyphens, no leading/
29
- * trailing/consecutive hyphens.
27
+ * The resolved name must conform to the Agent Skills spec: 1–64 lowercase
28
+ * alphanumeric characters and hyphens, no leading/trailing/consecutive
29
+ * hyphens.
30
30
  */
31
31
  name: string;
32
32
  /** Human-readable description of what the CLI does */
@@ -67,6 +67,18 @@ interface SkillMeta {
67
67
  * @example "Bash(my-cli *) Read Grep"
68
68
  */
69
69
  allowedTools?: string;
70
+ /**
71
+ * Additional top-level instructions rendered into `SKILL.md`.
72
+ *
73
+ * Use this for plugin- or product-specific guidance that should be visible
74
+ * before agents inspect individual command documentation files.
75
+ *
76
+ * **Note:** When a `string` value contains markdown headings (e.g. `## Foo`),
77
+ * they are rendered at the same level as `## General Guidance`, not nested
78
+ * under it. Use a `string[]` of plain instructions to avoid unintended
79
+ * heading hierarchy.
80
+ */
81
+ instructions?: string | string[];
70
82
  }
71
83
  /** Supported agent targets for skill installation. */
72
84
  type AgentTarget = "amp" | "adal" | "antigravity" | "augment" | "claude-code" | "cline" | "codebuddy" | "codex" | "command-code" | "continue" | "cortex" | "crush" | "cursor" | "droid" | "gemini-cli" | "github-copilot" | "goose" | "iflow-cli" | "junie" | "kilo" | "kimi-cli" | "kiro-cli" | "kode" | "mcpjam" | "mistral-vibe" | "mux" | "neovate" | "opencode" | "openclaw" | "openhands" | "pi" | "pochi" | "qoder" | "qwen-code" | "replit" | "roo" | "trae" | "trae-cn" | "windsurf" | "zencoder";
@@ -79,9 +91,9 @@ type SkillInstallMode = "auto" | "symlink" | "copy";
79
91
  /**
80
92
  * Top-level options for generating a skill bundle from a command tree.
81
93
  *
82
- * The `meta.name` value is automatically prefixed with `use-` for all output
83
- * paths and metadata. For example, `name: "my-cli"` produces skill directories
84
- * named `use-my-cli/` and sets the manifest/frontmatter name to `"use-my-cli"`.
94
+ * The `meta.name` value is used directly for all output paths and metadata.
95
+ * For example, `name: "my-cli"` produces skill directories named `my-cli/`
96
+ * and sets the manifest/frontmatter name to `"my-cli"`.
85
97
  *
86
98
  * @example
87
99
  * ```ts
@@ -91,7 +103,7 @@ type SkillInstallMode = "auto" | "symlink" | "copy";
91
103
  * await generateSkill({
92
104
  * command: rootCommand,
93
105
  * meta: {
94
- * name: "my-cli", // output: use-my-cli/
106
+ * name: "my-cli", // output: my-cli/
95
107
  * description: "CLI tool for managing widgets",
96
108
  * version: "1.0.0",
97
109
  * },
@@ -254,6 +266,30 @@ interface SkillPluginOptions {
254
266
  */
255
267
  autoUpdate?: boolean;
256
268
  /**
269
+ * Additional top-level instructions rendered into the generated `SKILL.md`.
270
+ *
271
+ * **Note:** When a `string` value contains markdown headings (e.g. `## Foo`),
272
+ * they are rendered at the same level as `## General Guidance`, not nested
273
+ * under it. Use a `string[]` of plain instructions to avoid unintended
274
+ * heading hierarchy.
275
+ */
276
+ instructions?: string | string[];
277
+ /** License name or reference emitted in SKILL.md frontmatter. */
278
+ license?: string;
279
+ /**
280
+ * Space-delimited list of pre-approved tools the skill may use.
281
+ *
282
+ * @example "Bash(my-cli *) Read Grep"
283
+ */
284
+ allowedTools?: string;
285
+ /** Environment requirements or compatibility notes (max 500 chars). */
286
+ compatibility?: string;
287
+ /**
288
+ * When `true`, prevents agents from automatically loading this skill.
289
+ * @default false
290
+ */
291
+ disableModelInvocation?: boolean;
292
+ /**
257
293
  * Register an interactive skill management subcommand on the root command.
258
294
  *
259
295
  * The command presents a single multiselect prompt listing all detected
@@ -293,6 +329,27 @@ declare function detectInstalledAgents(options?: string | DetectInstalledAgentsO
293
329
  * Resolves the canonical skill bundle path used by Crust.
294
330
  */
295
331
  declare function resolveCanonicalSkillPath(scope: Scope, name: string): string;
332
+ import { CommandNode as CommandNode2 } from "@crustjs/core";
333
+ import { Crust } from "@crustjs/core";
334
+ /**
335
+ * Agent-oriented instructions attached to a command for skills rendering.
336
+ */
337
+ interface SkillCommandAnnotations {
338
+ /** Additional prompt guidance rendered into the command's markdown file */
339
+ instructions?: string[];
340
+ }
341
+ type SkillCommandTarget = CommandNode2 | Crust<any, any, any>;
342
+ /**
343
+ * Attaches agent-facing instructions to a command definition without changing
344
+ * the public `@crustjs/core` API surface.
345
+ *
346
+ * The instructions are stored on the internal command node using an enumerable
347
+ * symbol so they survive Crust's immutable clone/spread builder operations.
348
+ *
349
+ * Duplicate instructions are silently deduplicated — calling `annotate()` again
350
+ * with the same text is a safe no-op.
351
+ */
352
+ declare function annotate<T extends SkillCommandTarget>(target: T, annotations: string | string[] | SkillCommandAnnotations): T;
296
353
  /** Details about the conflict between an existing skill and an incoming one. */
297
354
  interface SkillConflictDetails {
298
355
  /** The agent where the conflict was detected */
@@ -330,23 +387,23 @@ declare class SkillConflictError extends Error {
330
387
  /**
331
388
  * Validates a resolved skill name against the Agent Skills specification.
332
389
  *
333
- * @param name - The resolved skill name to validate (already has `use-` prefix)
390
+ * @param name - The resolved skill name to validate
334
391
  * @returns `true` if valid, `false` otherwise
335
392
  */
336
393
  declare function isValidSkillName(name: string): boolean;
337
394
  /**
338
- * Resolves the canonical skill name by applying the `use-` prefix.
395
+ * Resolves the canonical current skill name.
339
396
  *
340
397
  * All generated output (directory names, crust.json metadata, SKILL.md content)
341
- * uses the resolved name. Consumers pass the raw CLI name (e.g. `"my-cli"`),
342
- * and this function returns the prefixed form (e.g. `"use-my-cli"`).
398
+ * uses the resolved name directly. Consumers pass the raw CLI name
399
+ * (e.g. `"my-cli"`), and this function returns that same canonical name.
343
400
  *
344
401
  * @param name - The raw CLI tool name
345
- * @returns The prefixed skill name
402
+ * @returns The canonical skill name
346
403
  *
347
404
  * @example
348
405
  * ```ts
349
- * resolveSkillName("my-cli"); // "use-my-cli"
406
+ * resolveSkillName("my-cli"); // "my-cli"
350
407
  * ```
351
408
  */
352
409
  declare function resolveSkillName(name: string): string;
@@ -442,4 +499,4 @@ import { CrustPlugin } from "@crustjs/core";
442
499
  * ```
443
500
  */
444
501
  declare function skillPlugin(options: SkillPluginOptions): CrustPlugin;
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 };
502
+ export { uninstallSkill, skillStatus, skillPlugin, resolveSkillName, resolveCanonicalSkillPath, isValidSkillName, isUniversalAgent, getUniversalAgents, getAdditionalAgents, generateSkill, detectInstalledAgents, annotate, UninstallStatus, UninstallResult, UninstallOptions, StatusResult, StatusOptions, SkillPluginOptions, SkillMeta, SkillInstallMode, SkillConflictError, SkillConflictDetails, SkillCommandAnnotations, Scope, InstallStatus, GenerateResult, GenerateOptions, AgentTarget, AgentResult, AgentClass };
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // @bun
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};
2
+ import{accessSync as aQ,constants as nQ}from"fs";import{homedir as e}from"os";import{delimiter as tQ,join as Y}from"path";var k=Y(".agents","skills"),eQ=Y(".crust","skills");function VQ(Q){if(Q!==e())return Y(Q,".config");let Z=process.env.XDG_CONFIG_HOME?.trim();return Z&&Z.length>0?Z:Y(Q,".config")}function j(Q){return Y(Q,".agents","skills")}function QZ(Q){return Y(Q,".crust","skills")}var C={amp:{label:"Amp",class:"universal",projectSkillsDir:k,globalSkillsDir:j},adal:{label:"AdaL",class:"additional",projectSkillsDir:Y(".adal","skills"),globalSkillsDir:(Q)=>Y(Q,".adal","skills"),detectCommands:["adal"]},antigravity:{label:"Antigravity",class:"additional",projectSkillsDir:Y(".agent","skills"),globalSkillsDir:(Q)=>Y(Q,".gemini","antigravity","skills"),detectCommands:["antigravity"]},augment:{label:"Augment",class:"additional",projectSkillsDir:Y(".augment","skills"),globalSkillsDir:(Q)=>Y(Q,".augment","skills"),detectCommands:["augment"]},"claude-code":{label:"Claude Code",class:"additional",projectSkillsDir:Y(".claude","skills"),globalSkillsDir:(Q)=>Y(process.env.CLAUDE_CONFIG_DIR?.trim()||Y(Q,".claude"),"skills"),detectCommands:["claude","claude-code"]},cline:{label:"Cline",class:"universal",projectSkillsDir:k,globalSkillsDir:j},codebuddy:{label:"CodeBuddy",class:"additional",projectSkillsDir:Y(".codebuddy","skills"),globalSkillsDir:(Q)=>Y(Q,".codebuddy","skills"),detectCommands:["codebuddy"]},codex:{label:"Codex",class:"universal",projectSkillsDir:k,globalSkillsDir:j},"command-code":{label:"Command Code",class:"additional",projectSkillsDir:Y(".commandcode","skills"),globalSkillsDir:(Q)=>Y(Q,".commandcode","skills"),detectCommands:["command-code","commandcode"]},continue:{label:"Continue",class:"additional",projectSkillsDir:Y(".continue","skills"),globalSkillsDir:(Q)=>Y(Q,".continue","skills"),detectCommands:["continue"]},cortex:{label:"Cortex Code",class:"additional",projectSkillsDir:Y(".cortex","skills"),globalSkillsDir:(Q)=>Y(Q,".snowflake","cortex","skills"),detectCommands:["cortex"]},crush:{label:"Crush",class:"additional",projectSkillsDir:Y(".crush","skills"),globalSkillsDir:(Q)=>Y(VQ(Q),"crush","skills"),detectCommands:["crush"]},cursor:{label:"Cursor",class:"universal",projectSkillsDir:k,globalSkillsDir:j},droid:{label:"Droid",class:"additional",projectSkillsDir:Y(".factory","skills"),globalSkillsDir:(Q)=>Y(Q,".factory","skills"),detectCommands:["droid"]},"gemini-cli":{label:"Gemini CLI",class:"universal",projectSkillsDir:k,globalSkillsDir:j},"github-copilot":{label:"GitHub Copilot",class:"universal",projectSkillsDir:k,globalSkillsDir:j},goose:{label:"Goose",class:"additional",projectSkillsDir:Y(".goose","skills"),globalSkillsDir:(Q)=>Y(VQ(Q),"goose","skills"),detectCommands:["goose"]},"iflow-cli":{label:"iFlow CLI",class:"additional",projectSkillsDir:Y(".iflow","skills"),globalSkillsDir:(Q)=>Y(Q,".iflow","skills"),detectCommands:["iflow","iflow-cli"]},junie:{label:"Junie",class:"additional",projectSkillsDir:Y(".junie","skills"),globalSkillsDir:(Q)=>Y(Q,".junie","skills"),detectCommands:["junie"]},kilo:{label:"Kilo Code",class:"additional",projectSkillsDir:Y(".kilocode","skills"),globalSkillsDir:(Q)=>Y(Q,".kilocode","skills"),detectCommands:["kilo","kilocode"]},"kimi-cli":{label:"Kimi Code CLI",class:"universal",projectSkillsDir:k,globalSkillsDir:j},"kiro-cli":{label:"Kiro CLI",class:"additional",projectSkillsDir:Y(".kiro","skills"),globalSkillsDir:(Q)=>Y(Q,".kiro","skills"),detectCommands:["kiro","kiro-cli"]},kode:{label:"Kode",class:"additional",projectSkillsDir:Y(".kode","skills"),globalSkillsDir:(Q)=>Y(Q,".kode","skills"),detectCommands:["kode"]},mcpjam:{label:"MCPJam",class:"additional",projectSkillsDir:Y(".mcpjam","skills"),globalSkillsDir:(Q)=>Y(Q,".mcpjam","skills"),detectCommands:["mcpjam"]},"mistral-vibe":{label:"Mistral Vibe",class:"additional",projectSkillsDir:Y(".vibe","skills"),globalSkillsDir:(Q)=>Y(Q,".vibe","skills"),detectCommands:["mistral-vibe","vibe"]},mux:{label:"Mux",class:"additional",projectSkillsDir:Y(".mux","skills"),globalSkillsDir:(Q)=>Y(Q,".mux","skills"),detectCommands:["mux"]},neovate:{label:"Neovate",class:"additional",projectSkillsDir:Y(".neovate","skills"),globalSkillsDir:(Q)=>Y(Q,".neovate","skills"),detectCommands:["neovate"]},opencode:{label:"OpenCode",class:"universal",projectSkillsDir:k,globalSkillsDir:j},openclaw:{label:"OpenClaw",class:"additional",projectSkillsDir:"skills",globalSkillsDir:(Q)=>Y(Q,".openclaw","skills"),detectCommands:["openclaw"]},openhands:{label:"OpenHands",class:"additional",projectSkillsDir:Y(".openhands","skills"),globalSkillsDir:(Q)=>Y(Q,".openhands","skills"),detectCommands:["openhands"]},pi:{label:"Pi",class:"additional",projectSkillsDir:Y(".pi","skills"),globalSkillsDir:(Q)=>Y(Q,".pi","agent","skills"),detectCommands:["pi"]},pochi:{label:"Pochi",class:"additional",projectSkillsDir:Y(".pochi","skills"),globalSkillsDir:(Q)=>Y(Q,".pochi","skills"),detectCommands:["pochi"]},qoder:{label:"Qoder",class:"additional",projectSkillsDir:Y(".qoder","skills"),globalSkillsDir:(Q)=>Y(Q,".qoder","skills"),detectCommands:["qoder"]},"qwen-code":{label:"Qwen Code",class:"additional",projectSkillsDir:Y(".qwen","skills"),globalSkillsDir:(Q)=>Y(Q,".qwen","skills"),detectCommands:["qwen","qwen-code"]},replit:{label:"Replit",class:"universal",projectSkillsDir:k,globalSkillsDir:j},roo:{label:"Roo Code",class:"additional",projectSkillsDir:Y(".roo","skills"),globalSkillsDir:(Q)=>Y(Q,".roo","skills"),detectCommands:["roo","roo-code"]},trae:{label:"Trae",class:"additional",projectSkillsDir:Y(".trae","skills"),globalSkillsDir:(Q)=>Y(Q,".trae","skills"),detectCommands:["trae"]},"trae-cn":{label:"Trae CN",class:"additional",projectSkillsDir:Y(".trae","skills"),globalSkillsDir:(Q)=>Y(Q,".trae-cn","skills"),detectCommands:["trae-cn","trae"]},windsurf:{label:"Windsurf",class:"additional",projectSkillsDir:Y(".windsurf","skills"),globalSkillsDir:(Q)=>Y(Q,".codeium","windsurf","skills"),detectCommands:["windsurf"]},zencoder:{label:"Zencoder",class:"additional",projectSkillsDir:Y(".zencoder","skills"),globalSkillsDir:(Q)=>Y(Q,".zencoder","skills"),detectCommands:["zencoder"]}},u=Object.keys(C),y=Object.fromEntries(u.map((Q)=>[Q,C[Q].label]));function S(){return u.filter((Q)=>C[Q].class==="universal")}function h(){return u.filter((Q)=>C[Q].class==="additional")}function ZZ(Q){return C[Q].class==="universal"}async function QQ(Q){let Z=typeof Q==="string"?{home:Q}:Q??{},X=Z.cwd??process.cwd(),$=Z.commandChecker??((H)=>Promise.resolve(XZ(H))),W=[];for(let H of h()){let B=C[H].detectCommands??[],z=!1;for(let J of B)if(await $(J,X)){z=!0;break}if(z)W.push(H)}return W}function O(Q,Z,X){let $=C[Q];if(Z==="project")return Y(process.cwd(),$.projectSkillsDir,X);return Y($.globalSkillsDir(e()),X)}function P(Q,Z){if(Q==="project")return Y(process.cwd(),eQ,Z);return Y(QZ(e()),Z)}function XZ(Q){let X=(process.env.PATH??"").split(tQ).filter((H)=>H.length>0),$=process.platform==="win32",W=$?(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";").filter((H)=>H.length>0):[];for(let H of X){if(!$&&MQ(Y(H,Q)))return!0;if($){for(let B of W)if(MQ(Y(H,Q+B)))return!0}}return!1}function MQ(Q){try{return aQ(Q,nQ.X_OK),!0}catch{return!1}}import{Crust as $Z}from"@crustjs/core";function s(Q){if(Q===void 0)return[];return(Array.isArray(Q)?Q:[Q]).flatMap((X)=>X.split(/\r?\n/)).map((X)=>X.trim()).filter((X)=>X.length>0)}function TQ(Q){let Z=Q?.trim();if(!Z)return[];return Z.split(/\r?\n/)}function ZQ(Q){return Q.length>0}var _Q=Symbol("crust.skill.commandAnnotations");function WZ(Q){return Q instanceof $Z?Q._node:Q}function HZ(Q,Z){let X=WZ(Q),$=s(typeof Z==="string"||Array.isArray(Z)?Z:Z.instructions??[]);if($.length===0)return Q;let W=XQ(X)?.instructions??[],H=[...new Set([...W,...$])];return Object.defineProperty(X,_Q,{value:{instructions:H},enumerable:!0,configurable:!0}),Q}function XQ(Q){let Z=Q[_Q];if(!Z?.instructions||Z.instructions.length===0)return;return{instructions:[...Z.instructions]}}class I extends Error{name="SkillConflictError";details;constructor(Q){let Z=`Skill conflict for agent "${Q.agent}": directory "${Q.outputDir}" already exists but was not created by Crust (no crust.json found). Delete or rename the conflicting skill to resolve.`;super(Z);this.details=Q}}import{lstat as NZ,mkdir as hQ,readlink as LZ,realpath as PZ,rm as m,symlink as CZ,writeFile as yZ}from"fs/promises";import{dirname as pQ,join as SQ}from"path";function bQ(Q){return OQ(Q,[])}function OQ(Q,Z){let X=YZ(Q.meta.name),$=[...Z,X],W=qZ(Q.args),H=JZ(Q.effectiveFlags),B=KZ(Q.subCommands,$),z=XQ(Q);return{name:X,path:$,description:Q.meta.description,usage:Q.meta.usage,instructions:z?.instructions,runnable:typeof Q.run==="function",args:W,flags:H,children:B}}function YZ(Q){return Q.trim().toLowerCase()}function qZ(Q){if(!Q||Q.length===0)return[];return Q.map(BZ)}function BZ(Q){let Z={name:Q.name,type:Q.type,required:Q.required===!0,variadic:Q.variadic===!0};if(Q.description!==void 0)Z.description=Q.description;if(Q.default!==void 0)Z.default=RQ(Q.default);return Z}function JZ(Q){if(!Q)return[];return Object.keys(Q).sort().map((X)=>{return zZ(X,Q[X])})}function zZ(Q,Z){let X={name:Q,type:Z.type,required:Z.required===!0,multiple:Z.multiple===!0,short:Z.short,aliases:Z.aliases?[...Z.aliases].sort():[]};if(Z.description!==void 0)X.description=Z.description;if(Z.default!==void 0)X.default=RQ(Z.default);return X}function KZ(Q,Z){return Object.keys(Q).sort().map(($)=>{return OQ(Q[$],Z)})}function RQ(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function c(Q){if(/[:#[\]{}&*!|>'"`,@?\\]|^\s|\s$|^---|[\n\r]/.test(Q))return`"${Q.replace(/\\/g,"\\\\").replace(/"/g,"\\\"").replace(/\n/g,"\\n").replace(/\r/g,"\\r")}"`;return Q}function EQ(Q){return Q.replace(/(?<!\\)\|/g,"\\|")}function kQ(Q,Z){let X=[],$=jQ(Q);X.push({path:"SKILL.md",content:xZ(Q,Z,$)});for(let W of $){let H=D(W),B=W.children.length>0?UZ(W,Q):wZ(W,Q);X.push({path:H,content:B})}return X}function jQ(Q){let Z=[Q];for(let X of Q.children)Z.push(...jQ(X));return Z}function D(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function WQ(Q){return Q.path.join(" ")}function $Q(Q,Z){let X=Q.split("/").slice(0,-1),$=Z.split("/"),W=0;while(W<X.length&&W<$.length&&X[W]===$[W])W++;let H=X.length-W,B=$.slice(W);if(H===0)return B.join("/");return[...Array.from({length:H},()=>".."),...B].join("/")}function xZ(Q,Z,X){let $=[];if($.push("---"),$.push(`name: ${c(Z.name)}`),$.push(`description: ${c(Z.description)}`),Z.license)$.push(`license: ${c(Z.license)}`);if(Z.compatibility)$.push(`compatibility: ${c(Z.compatibility)}`);if(Z.disableModelInvocation)$.push("disable-model-invocation: true");if(Z.allowedTools)$.push(`allowed-tools: ${c(Z.allowedTools)}`);if($.push("metadata:"),$.push(` version: "${Z.version}"`),$.push("---"),$.push(""),$.push(`# ${Z.name}`),$.push(""),Q.description)$.push(Q.description),$.push("");$.push(`Use this skill when you need accurate help with \`${Z.name}\` commands, including command selection, syntax, arguments, flags, defaults, and subcommands.`),$.push("");let W=EZ(Z.instructions);if($.push("## How to Use This Skill"),$.push(""),$.push("1. Find the command that best matches the user's task from the Command Reference below"),$.push("2. Check the `Type` column before suggesting execution: `runnable` and `runnable, group` commands can be executed, while `group` commands are organizational only"),$.push("3. Read only the linked file or files you need from `commands/`"),$.push("4. Before answering a command-specific question or suggesting a command, read that command's file"),$.push("5. Treat the command file as the source of truth for usage, arguments, flags, aliases, and defaults"),$.push("6. If a flag, argument, alias, or default is not documented there, say it is not documented instead of guessing"),$.push(""),ZQ(W))$.push("## General Guidance"),$.push(""),$.push(...W),$.push("");if($.push("## Command Reference"),$.push(""),$.push("Use this table to locate the command file you need."),$.push(""),$.push(...GZ(X)),$.push(""),Q.runnable){$.push("## Usage"),$.push("");let H=D(Q);$.push(`The root command is directly executable. See [${Q.name}](${H}) for usage details.`),$.push("")}return $.join(`
3
+ `)}function GZ(Q){let Z=[];Z.push("| Command | Type | Documentation |"),Z.push("| ------- | ---- | ------------- |");for(let X of Q){let $=WQ(X),W=D(X),H=FZ(X);Z.push(`| \`${$}\` | ${H} | [${W}](${W}) |`)}return Z}function FZ(Q){if(Q.runnable&&Q.children.length>0)return"runnable, group";if(Q.runnable)return"runnable";return"group"}function wZ(Q,Z){let X=[];return X.push(...IQ(Q)),X.push(...NQ(Q)),X.push(...LQ(Q)),X.push(...CQ(Q,Z)),X.join(`
4
+ `)}function UZ(Q,Z){let X=[],$=D(Q);if(X.push(...IQ(Q)),X.push(...NQ(Q)),Q.runnable)X.push(...LQ(Q));return X.push(...MZ(Q,$)),X.push(...CQ(Q,Z)),X.join(`
5
+ `)}function VZ(Q){let Z=[...Q.path];for(let X of Q.args)if(X.variadic)Z.push(X.required?`<${X.name}...>`:`[${X.name}...]`);else Z.push(X.required?`<${X.name}>`:`[${X.name}]`);if(Q.flags.length>0)Z.push("[options]");return Z.join(" ")}function IQ(Q){let Z=[`# \`${WQ(Q)}\``,""];if(Q.description)Z.push(Q.description,"");return Z}function NQ(Q){let Z=Q.instructions??[];if(!ZQ(Z))return[];return["## Command Instructions","",...PQ(Z),""]}function LQ(Q){let Z=["## Usage","","```",Q.usage??VZ(Q),"```",""];if(Q.args.length>0)Z.push("## Arguments","",...TZ(Q.args),"");if(Q.flags.length>0)Z.push("## Flags","",...bZ(Q.flags),"");return Z.push("## Command Documentation Authority","","Only arguments, flags, options, aliases, and defaults documented in this file are supported for this command.","Do not infer or invent additional command-line options.",""),Z}function MZ(Q,Z){let X=["## Subcommands",""];for(let $ of Q.children){let W=D($),H=$Q(Z,W),B=$.description?` - ${$.description}`:"";X.push(`- [\`${$.name}\`](${H})${B}`)}return X.push(""),X}function TZ(Q){let Z=[];Z.push("| Argument | Type | Required | Description |"),Z.push("| -------- | ---- | -------- | ----------- |");for(let X of Q){let $=X.variadic?`${X.name}...`:X.name,W=X.required?"Yes":"No",H=EQ(_Z(X));Z.push(`| \`${$}\` | ${X.type} | ${W} | ${H} |`)}return Z}function _Z(Q){let Z=[];if(Q.description)Z.push(Q.description);if(Q.default!==void 0)Z.push(`Default: \`${Q.default}\``);return Z.join(". ")||"-"}function bZ(Q){let Z=[];Z.push("| Flag | Type | Required | Description |"),Z.push("| ---- | ---- | -------- | ----------- |");for(let X of Q){let $=OZ(X),W=X.required?"Yes":"No",H=EQ(RZ(X));Z.push(`| ${$} | ${X.type} | ${W} | ${H} |`)}return Z}function OZ(Q){let Z=[`\`--${Q.name}\``];if(Q.short)Z.push(`\`-${Q.short}\``);for(let X of Q.aliases)Z.push(`\`--${X}\``);return Z.join(", ")}function RZ(Q){let Z=[];if(Q.description)Z.push(Q.description);if(Q.multiple)Z.push("Can be specified multiple times");if(Q.default!==void 0)Z.push(`Default: \`${Q.default}\``);return Z.join(". ")||"-"}function PQ(Q){return Q.map((Z)=>`- ${Z}`)}function EZ(Q){if(typeof Q==="string")return TQ(Q);return PQ(s(Q))}function CQ(Q,Z){let X=[],$=D(Q);if(X.push("---"),X.push(""),Q.path.length>1){let H=Q.path.slice(0,-1),B=yQ(Z,H);if(B){let z=D(B),J=$Q($,z),K=WQ(B);X.push(`Parent: [\`${K}\`](${J})`),X.push("")}}let W=$Q($,"SKILL.md");return X.push(`[Skill Overview](${W})`),X.push(""),X}function yQ(Q,Z){if(kZ(Q.path,Z))return Q;for(let X of Q.children){let $=yQ(X,Z);if($)return $}return}function kZ(Q,Z){if(Q.length!==Z.length)return!1;for(let X=0;X<Q.length;X++)if(Q[X]!==Z[X])return!1;return!0}import{readFile as jZ}from"fs/promises";import{join as IZ}from"path";var HQ="crust.json";async function A(Q){try{let Z=await jZ(IZ(Q,HQ),"utf-8"),X=JSON.parse(Z);if(typeof X==="object"&&X!==null&&"version"in X&&typeof X.version==="string")return X.version;return null}catch{return null}}var SZ="auto",gQ=/^[a-z0-9]+(-[a-z0-9]+)*$/;function uQ(Q){return Q.length>=1&&Q.length<=64&&gQ.test(Q)}function i(Q){return Q}function BQ(Q){return Q.startsWith("use-")?Q:`use-${Q}`}async function p(Q){let{command:Z,meta:X,agents:$,scope:W="global",clean:H=!0,force:B=!1,installMode:z=SZ}=Q,J=i(X.name),K=BQ(X.name);if(!uQ(J))throw Error(`Invalid skill name "${J}": must be 1\u201364 lowercase `+`alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens. Pattern: ${gQ.source}`);let x=$[0];if(!x)return{agents:[]};let G={...X,name:J},U=bQ(Z),N=kQ(U,G),M=pZ(G),b=[...N,...M].sort((q,F)=>q.path<F.path?-1:q.path>F.path?1:0),R=b.map((q)=>q.path),T=new Map;for(let q of $){let F=O(q,W,G.name),w=T.get(F);if(w)w.push(q);else T.set(F,[q])}let _=P(W,G.name),E=P(W,K),L=new Map;for(let[q,F]of T){let w=F[0];if(!w)continue;L.set(q,await KQ({outputDir:q,legacyOutputDir:O(w,W,K),canonicalOutputDir:_,legacyCanonicalOutputDir:E}))}let l=await A(_);if((await zQ(_,_)).exists&&l===null&&!B)throw new I({agent:x,outputDir:_});let v=l!==G.version;if(v){if(H)await xQ(_);await cQ(_,b)}let f=[];for(let[q,F]of T){let w=F[0];if(!w)continue;let V=L.get(q);if(!V)continue;if(V.current.inspection.exists&&!V.current.isCrustManaged&&!B)throw new I({agent:w,outputDir:q});let n=await AZ({outputDir:q,canonicalOutputDir:_,allFiles:b,clean:H,installMode:z,inspection:V.current.inspection,installedVersion:V.preferredVersion,currentVersion:G.version}),iQ=await fZ(V),t=DZ({installedVersion:V.preferredVersion,currentVersion:G.version,canonicalChanged:v,pathChanged:n||iQ||V.preferredOutputDir!==q});for(let oQ of F)f.push({agent:oQ,outputDir:q,files:t==="up-to-date"?[]:R,status:t,previousVersion:t==="updated"?V.preferredVersion??void 0:void 0})}{let q=await A(E);if(E!==_&&q!==null&&!await qQ(K,W))await m(E,{recursive:!0,force:!0})}return{agents:f}}async function JQ(Q){let{name:Z,agents:X,scope:$="global"}=Q,W=i(Z),H=BQ(Z),B=P($,W),z=P($,H),J=[],K=new Map;for(let x of X){let G=O(x,$,W),U=K.get(G);if(U)U.push(x);else K.set(G,[x])}for(let[x,G]of K){let U=G[0];if(!U)continue;let N=O(U,$,H),M=await KQ({outputDir:x,legacyOutputDir:N,canonicalOutputDir:B,legacyCanonicalOutputDir:z}),b=await YQ(M.current),R=M.legacy.outputDir!==M.current.outputDir?await YQ(M.legacy):!1,T=b||R,_=b?x:R?N:x;for(let E of G)J.push({agent:E,outputDir:_,status:T?"removed":"not-found"})}if(await A(B)!==null&&!await qQ(W,$))await m(B,{recursive:!0,force:!0});{let x=await A(z);if(z!==B&&x!==null&&!await qQ(H,$))await m(z,{recursive:!0,force:!0})}return{agents:J}}async function d(Q){let{name:Z,agents:X,scope:$="global"}=Q,W=i(Z),H=BQ(Z),B=[],z=new Map;for(let J of X){let K=O(J,$,W),x=z.get(K);if(x)x.push(J);else z.set(K,[J])}for(let[J,K]of z){let x=K[0];if(!x)continue;let G=O(x,$,H),U=P($,W),N=P($,H),M=await KQ({outputDir:J,legacyOutputDir:G,canonicalOutputDir:U,legacyCanonicalOutputDir:N}),b=M.preferredOutputDir??J,R=M.preferredVersion;for(let T of K)B.push({agent:T,outputDir:b,installed:R!==null,version:R??void 0})}return{agents:B}}function DZ(Q){let{installedVersion:Z,currentVersion:X,canonicalChanged:$,pathChanged:W}=Q;if(Z===null)return"installed";if(Z===X&&!$&&!W)return"up-to-date";return"updated"}async function AZ(Q){let{outputDir:Z,canonicalOutputDir:X,allFiles:$,clean:W,installMode:H,inspection:B,installedVersion:z,currentVersion:J}=Q;if(H==="copy")return DQ({outputDir:Z,allFiles:$,clean:W,inspection:B,installedVersion:z,currentVersion:J});try{return await vZ({outputDir:Z,canonicalOutputDir:X,inspection:B})}catch(K){if(H==="symlink")throw Error(`Failed to create symlink at "${Z}" (installMode: symlink).`,{cause:K});let x=await zQ(Z,X);return DQ({outputDir:Z,allFiles:$,clean:W,inspection:x,installedVersion:z,currentVersion:J})}}async function DQ(Q){let{outputDir:Z,allFiles:X,clean:$,inspection:W,installedVersion:H,currentVersion:B}=Q;if(!(!W.exists||W.isSymlink||H!==B))return!1;if(W.isSymlink||$)await xQ(Z);return await cQ(Z,X),!0}async function vZ(Q){let{outputDir:Z,canonicalOutputDir:X,inspection:$}=Q;if($.exists&&$.isSymlink&&$.pointsToCanonical)return!1;if($.exists)await xQ(Z);return await hZ(X,Z),!0}async function zQ(Q,Z){let X;try{X=await NZ(Q)}catch{return{exists:!1,isSymlink:!1,pointsToCanonical:!1}}let $=process.platform==="win32"&&X.isDirectory()&&await fQ(Q)!==null;if(!(X.isSymbolicLink()||$))return{exists:!0,isSymlink:!1,pointsToCanonical:!1};let[H,B,z]=await Promise.all([vQ(Q),vQ(Z),fQ(Q)]);return{exists:!0,isSymlink:!0,pointsToCanonical:H!==null&&B!==null&&H===B||z===Z}}async function AQ(Q,Z){let[X,$]=await Promise.all([A(Q),zQ(Q,Z)]),W=X!==null||$.exists&&$.isSymlink&&$.pointsToCanonical;return{outputDir:Q,version:X,inspection:$,isCrustManaged:W}}async function KQ(Q){let{outputDir:Z,legacyOutputDir:X,canonicalOutputDir:$,legacyCanonicalOutputDir:W}=Q,H=await AQ(Z,$),B=X===Z?H:await AQ(X,W);if(H.isCrustManaged)return{current:H,legacy:B,preferredVersion:H.version,preferredOutputDir:H.outputDir};if(B.isCrustManaged)return{current:H,legacy:B,preferredVersion:B.version,preferredOutputDir:B.outputDir};return{current:H,legacy:B,preferredVersion:null,preferredOutputDir:null}}async function YQ(Q){if(!Q.isCrustManaged||!Q.inspection.exists)return!1;return await m(Q.outputDir,{recursive:!0,force:!0}),!0}async function fZ(Q){if(Q.legacy.outputDir===Q.current.outputDir)return!1;return YQ(Q.legacy)}async function vQ(Q){try{return await PZ(Q)}catch{return null}}async function fQ(Q){try{return await LZ(Q)}catch{return null}}async function hZ(Q,Z){await hQ(pQ(Z),{recursive:!0});let X=process.platform==="win32"?"junction":"dir";await CZ(Q,Z,X)}async function qQ(Q,Z){let X=new Set;for(let $ of u)X.add(O($,Z,Q));for(let $ of X)if(await A($)!==null)return!0;return!1}function pZ(Q){let Z={name:Q.name,description:Q.description,version:Q.version};return[{path:HQ,content:`${JSON.stringify(Z,null,"\t")}
6
+ `}]}async function xQ(Q){await m(Q,{recursive:!0,force:!0})}async function cQ(Q,Z){let X=new Set;for(let W of Z){let H=SQ(Q,W.path),B=pQ(H);X.add(B)}let $=[...X].sort();for(let W of $)await hQ(W,{recursive:!0});for(let W of Z){let H=SQ(Q,W.path);await yZ(H,W.content,"utf-8")}}import{Crust as dQ,VALIDATION_MODE_ENV as gZ}from"@crustjs/core";import{confirm as uZ,multiselect as cZ,select as mZ,spinner as r}from"@crustjs/prompts";import{bold as a,dim as g,yellow as rQ}from"@crustjs/style";var dZ="skill",lQ="global",o="__universal__";function rZ(Q){return Q==="global"||Q==="project"}async function sQ(Q,Z){if(Q!==void 0){if(!rZ(Q))throw Error(`Invalid --scope value: ${String(Q)}. Expected "project" or "global".`);return Q}if(Z.defaultScope)return Z.defaultScope;return mZ({message:"Select scope",choices:[{label:"Project",value:"project"},{label:"Global",value:"global"}],default:lQ})}function GQ(Q){let Z=new Set(S()),X=[];if(Q.some(($)=>Z.has($)))X.push("Universal");for(let $ of Q){if(Z.has($))continue;X.push(y[$])}return X}function mQ(Q){let Z=new Set(S()),X=[],$=Q.find((W)=>Z.has(W.agent));if($)X.push({label:"Universal",outputDir:$.outputDir});for(let W of Q){if(Z.has(W.agent))continue;X.push({label:y[W.agent],outputDir:W.outputDir})}return X}function FQ(Q,Z){return{name:Q.meta.name,description:Q.meta.description??"",version:Z.version,instructions:Z.instructions,license:Z.license,allowedTools:Z.allowedTools,compatibility:Z.compatibility,disableModelInvocation:Z.disableModelInvocation}}function wQ(Q,Z,X,$){if(!$.installed)return!1;let W=O(Q,Z,X.name);return $.version!==X.version||$.outputDir!==W}async function lZ(Q,Z){let X=[...S(),...h()];if(X.length===0)return;let $=FQ(Q,Z),W=["project","global"];for(let H of W){let z=(await d({name:$.name,agents:X,scope:H})).agents.filter((J)=>wQ(J.agent,H,$,J));if(z.length===0)continue;try{await r({message:`Updating ${H} skills...`,task:async({updateMessage:J})=>{let K=await p({command:Q,meta:$,agents:z.map((U)=>U.agent),scope:H,installMode:Z.installMode}),x=K.agents.filter((U)=>U.status==="updated").map((U)=>U.agent),G=GQ(x);if(G.length>0)J(`Updated skill "${$.name}" to v${$.version} for ${G.join(", ")} (${H})`);return K}})}catch(J){if(J instanceof I)console.warn(rQ(`Skill conflict: "${J.details.outputDir}" already exists but was not created by ${$.name}. Skipping auto-update for ${H}. Delete or rename the conflicting skill to resolve.`));else throw J}}}function sZ(Q){let Z;return{name:"skills",async setup(X,$){Z=X.rootCommand;let W=Q.command??dZ;if($.addSubCommand(Z,W,iZ(Z,Q,W)),process.env[gZ]==="1")return;if(X.argv[0]===W)return;if(Q.autoUpdate!==!1)await lZ(Z,Q)}}}function iZ(Q,Z,X){let $=oZ(Q,Z);return new dQ(X).meta({description:"Manage agent skill installations"}).flags({scope:{type:"string",description:"Install scope (project or global)"},all:{type:"boolean",description:"Install for all detected agents non-interactively (universal + detected)"}}).run(async(W)=>{let H=FQ(Q,Z),B=W.flags.all===!0,z=B?Z.defaultScope??lQ:await sQ(W.flags.scope,Z),J=await QQ(),K=S(),x=h(),G=await d({name:H.name,agents:[...K,...x],scope:z}),U=new Set(G.agents.filter((q)=>q.installed).map((q)=>q.agent)),N=new Set(J),M=new Map(G.agents.map((q)=>[q.agent,q])),b=x.filter((q)=>{if(N.has(q))return!0;return M.get(q)?.installed===!0}),R=b.filter((q)=>U.has(q)),T=[];if(K.length>0){let q=K[0];if(!q)throw Error("Expected at least one universal agent");let w=M.get(q)?.outputDir??"path unavailable";T.push({label:"Universal",value:o,hint:w});let V=K.map((n)=>y[n]).join(", ");console.log(g(`Agents supporting universal skills: ${V}`))}for(let q of b){let w=M.get(q)?.outputDir??"path unavailable";T.push({label:y[q],value:q,hint:w})}let _=K.length>0&&K.every((q)=>U.has(q)),E=[...R.filter((q)=>!K.includes(q))];if(_)E.unshift(o);let L;if(B)L=[...K,...b];else{let q=T.length===0?[]:await cZ({message:"Select agents to install skills for",choices:T,default:E,required:!1}),F=new Set(q.filter((w)=>w!==o));if(q.includes(o))for(let w of K)F.add(w);L=[...F]}let l=L.filter((q)=>!U.has(q)),UQ=L.filter((q)=>{let F=M.get(q);return F!==void 0&&wQ(q,z,H,F)}),v=[...U].filter((q)=>!L.includes(q)),f=[...l,...UQ];if(f.length>0)try{let q=await r({message:"Installing skills...",task:async()=>p({command:Q,meta:H,agents:f,scope:z,installMode:Z.installMode})});console.log(`
7
+ ${a(`Installed "${H.name}" v${H.version}`)}`);for(let F of mQ(q.agents))console.log(g(` ${F.label} \u2192 ${F.outputDir}`))}catch(q){if(q instanceof I)if(B?!0:await uZ({message:`"${q.details.outputDir}" already exists but was not created by Crust. Overwrite?`,default:!1})){let w=await r({message:"Overwriting skill...",task:async()=>p({command:Q,meta:H,agents:[q.details.agent],scope:z,force:!0,installMode:Z.installMode})});console.log(`
8
+ ${a(`Installed "${H.name}" v${H.version}`)}`);for(let V of mQ(w.agents))console.log(g(` ${V.label} \u2192 ${V.outputDir}`))}else console.log(g(`
9
+ Skipped ${y[q.details.agent]}`));else throw q}if(v.length>0){let F=(await r({message:"Removing skills...",task:async()=>JQ({name:H.name,agents:v,scope:z})})).agents.filter((V)=>V.status==="removed").map((V)=>V.agent),w=GQ(F);if(w.length>0)console.log(`
10
+ ${a(`Removed from ${w.join(", ")}`)}`)}if(f.length===0&&v.length===0)console.log(g("No changes."))}).command($)._node}function oZ(Q,Z){return new dQ("update").meta({description:"Update installed skills to latest version"}).flags({scope:{type:"string",description:"Update scope (project or global)"}}).run(async(X)=>{let $=await sQ(X.flags.scope,Z),W=[...S(),...h()],H=FQ(Q,Z),z=(await d({name:H.name,agents:W,scope:$})).agents.filter((J)=>wQ(J.agent,$,H,J));if(z.length===0){console.log(g(`No updates needed (${$}).`));return}try{let K=(await r({message:`Updating ${$} skills...`,task:async()=>p({command:Q,meta:H,agents:z.map((G)=>G.agent),scope:$,installMode:Z.installMode})})).agents.filter((G)=>G.status==="updated").map((G)=>G.agent),x=GQ(K);if(x.length>0)console.log(`
11
+ ${a(`Updated "${H.name}" to v${H.version} for ${x.join(", ")} (${$})`)}`)}catch(J){if(J instanceof I)console.warn(rQ(`Skipped ${y[J.details.agent]}: "${J.details.outputDir}" already exists but was not created by ${H.name}. Delete or rename the conflicting directory to resolve.`));else throw J}})}export{JQ as uninstallSkill,d as skillStatus,sZ as skillPlugin,i as resolveSkillName,P as resolveCanonicalSkillPath,uQ as isValidSkillName,ZZ as isUniversalAgent,S as getUniversalAgents,h as getAdditionalAgents,p as generateSkill,QQ as detectInstalledAgents,HZ as annotate,I as SkillConflictError};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crustjs/skills",
3
- "version": "0.0.16",
3
+ "version": "0.0.19",
4
4
  "description": "Agent skill generation from Crust command definitions",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,7 +39,8 @@
39
39
  "build": "bunup",
40
40
  "dev": "bunup --watch",
41
41
  "check:types": "tsc --noEmit",
42
- "test": "bun test"
42
+ "test": "bun test",
43
+ "publish": "bun publish --no-git-checks || true"
43
44
  },
44
45
  "dependencies": {
45
46
  "@crustjs/prompts": "0.0.9",
@@ -47,11 +48,11 @@
47
48
  },
48
49
  "devDependencies": {
49
50
  "@crustjs/config": "0.0.0",
50
- "@crustjs/core": "0.0.13",
51
+ "@crustjs/core": "0.0.15",
51
52
  "bunup": "^0.16.29"
52
53
  },
53
54
  "peerDependencies": {
54
- "@crustjs/core": "0.0.13",
55
+ "@crustjs/core": "0.0.15",
55
56
  "typescript": "^5"
56
57
  }
57
58
  }