@crustjs/skills 0.0.10 → 0.0.11
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 +1 -3
- package/dist/index.js +9 -10
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -282,7 +282,6 @@ Generated output goes to `<outDir>/skills/use-<name>/` (the `use-` prefix is app
|
|
|
282
282
|
```
|
|
283
283
|
skills/use-my-cli/
|
|
284
284
|
SKILL.md # Entrypoint — loaded by the agent
|
|
285
|
-
command-index.md # Maps all commands to documentation file paths
|
|
286
285
|
commands/ # Per-command documentation mirroring the CLI hierarchy
|
|
287
286
|
my-cli.md # Root command
|
|
288
287
|
serve.md # Subcommand
|
|
@@ -296,8 +295,7 @@ skills/use-my-cli/
|
|
|
296
295
|
|
|
297
296
|
| File | Purpose |
|
|
298
297
|
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
299
|
-
| `SKILL.md` | Agent entrypoint with YAML frontmatter
|
|
300
|
-
| `command-index.md` | Markdown table listing every command, its type (runnable/group), and documentation path. |
|
|
298
|
+
| `SKILL.md` | Agent entrypoint with YAML frontmatter and an embedded command reference table listing every command path, type (runnable/group), and documentation link. |
|
|
301
299
|
| `commands/*.md` | Per-command reference files. Leaf commands include usage, arguments, flags, defaults, and aliases. Group commands list subcommands with links. |
|
|
302
300
|
| `crust.json` | Crust-specific JSON metadata: name, description, version, entrypoint, and list of all command paths. Also serves as an ownership marker — its presence indicates the skill was generated by Crust. |
|
|
303
301
|
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{access as JQ}from"fs/promises";import{homedir as u}from"os";import{join as z}from"path";var
|
|
3
|
-
`)}function UQ(Q
|
|
4
|
-
`)}function
|
|
5
|
-
`)}function
|
|
6
|
-
`
|
|
7
|
-
|
|
8
|
-
${v(`Installed "${H.name}" v${H.version}`)}`);for(let
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
${v(`Removed from ${B.join(", ")}`)}`)}if(x.length===0&&V.length===0)console.log(P("No changes."))},X}export{N as uninstallSkill,T as skillStatus,uQ as skillPlugin,I as resolveSkillName,QQ as isValidSkillName,w as generateSkill,E as detectInstalledAgents,M as SkillConflictError};
|
|
2
|
+
import{access as JQ}from"fs/promises";import{homedir as u}from"os";import{join as z}from"path";var qQ=["claude-code","opencode"],V={"claude-code":"Claude Code",opencode:"OpenCode"};function S(Q,Z,X){let H=Z==="global"?u():process.cwd();switch(Q){case"claude-code":return z(H,".claude","skills",X);case"opencode":if(Z==="global")return z(H,".config","opencode","skills",X);return z(H,".opencode","skills",X)}}async function E(Q){let Z=typeof Q==="string"?{home:Q}:Q??{},X=Z.scope??"global",H=Z.home??u(),W=Z.cwd??process.cwd(),$=[];for(let J of qQ){let q=xQ(J,X,H,W),R=!1;for(let B of q)if(R=await JQ(B).then(()=>!0).catch(()=>!1),R)break;if(R)$.push(J)}return $}function xQ(Q,Z,X,H){if(Z==="project")switch(Q){case"claude-code":return[z(H,".claude"),z(X,".claude")];case"opencode":return[z(H,".opencode"),z(X,".config","opencode")]}switch(Q){case"claude-code":return[z(X,".claude")];case"opencode":return[z(X,".config","opencode")]}}class j extends Error{name="SkillConflictError";details;constructor(Q){let Z=`Skill conflict for agent "${Q.agent}": directory "${Q.outputDir}" already exists but was not created by Crust (no crust.json found). Delete or rename the conflicting skill to resolve.`;super(Z);this.details=Q}}import{access as n,mkdir as EQ,rm as a,writeFile as LQ}from"fs/promises";import{dirname as TQ,join as t}from"path";function p(Q){return f(Q,[])}function f(Q,Z){let X=RQ(Q.meta.name),H=[...Z,X],W=YQ(Q.args),$=KQ(Q.effectiveFlags),J=GQ(Q.subCommands,H);return{name:X,path:H,description:Q.meta.description,usage:Q.meta.usage,runnable:typeof Q.run==="function",args:W,flags:$,children:J}}function RQ(Q){return Q.trim().toLowerCase()}function YQ(Q){if(!Q||Q.length===0)return[];return Q.map(BQ)}function BQ(Q){let Z={name:Q.name,type:Q.type,required:Q.required===!0,variadic:Q.variadic===!0};if(Q.description!==void 0)Z.description=Q.description;if(Q.default!==void 0)Z.default=c(Q.default);return Z}function KQ(Q){if(!Q)return[];return Object.keys(Q).sort().map((X)=>{return OQ(X,Q[X])})}function OQ(Q,Z){let X={name:Q,type:Z.type,required:Z.required===!0,multiple:Z.multiple===!0,short:Z.short,aliases:Z.aliases?[...Z.aliases].sort():[]};if(Z.description!==void 0)X.description=Z.description;if(Z.default!==void 0)X.default=c(Z.default);return X}function GQ(Q,Z){return Object.keys(Q).sort().map((H)=>{return f(Q[H],Z)})}function c(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function _(Q){if(/[:#[\]{}&*!|>'"`,@?\\]|^\s|\s$|^---|[\n\r]/.test(Q))return`"${Q.replace(/\\/g,"\\\\").replace(/"/g,"\\\"").replace(/\n/g,"\\n").replace(/\r/g,"\\r")}"`;return Q}function g(Q){return Q.replace(/(?<!\\)\|/g,"\\|")}function m(Q,Z){let X=[],H=d(Q);X.push({path:"SKILL.md",content:zQ(Q,Z,H)});for(let W of H){let $=M(W),J=W.children.length>0?MQ(W,Q):jQ(W,Q);X.push({path:$,content:J})}return X}function d(Q){let Z=[Q];for(let X of Q.children)Z.push(...d(X));return Z}function M(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function L(Q){return Q.path.join(" ")}function A(Q,Z){let X=Q.split("/").slice(0,-1),H=Z.split("/"),W=0;while(W<X.length&&W<H.length&&X[W]===H[W])W++;let $=X.length-W,J=H.slice(W);if($===0)return J.join("/");return[...Array.from({length:$},()=>".."),...J].join("/")}function zQ(Q,Z,X){let H=[];if(H.push("---"),H.push(`name: ${_(Z.name)}`),H.push(`description: ${_(Z.description)}`),Z.license)H.push(`license: ${_(Z.license)}`);if(Z.compatibility)H.push(`compatibility: ${_(Z.compatibility)}`);if(Z.disableModelInvocation)H.push("disable-model-invocation: true");if(Z.allowedTools)H.push(`allowed-tools: ${_(Z.allowedTools)}`);if(H.push("metadata:"),H.push(` version: "${Z.version}"`),H.push("---"),H.push(""),H.push(`# ${Z.name}`),H.push(""),Q.description)H.push(Q.description),H.push("");let W=Z.name.startsWith("use-")?Z.name.slice(4):Z.name;if(H.push(`Use this skill when working with \`${W}\` commands, or when you need help with \`${W}\` syntax, flags, or subcommands.`),H.push(""),H.push("## Command Reference"),H.push(""),H.push("This table lists all commands and their documentation paths. **Do not read all command files at once.** Instead:"),H.push(""),H.push("1. Use the table below to find the relevant command"),H.push("2. Use the `Type` column to choose what to execute: commands labeled `runnable` (including `runnable, group`) are executable, while `group` commands are not"),H.push("3. Read only the specific file from the `commands/` directory that you need"),H.push(""),H.push(...UQ(X)),H.push(""),Q.runnable){H.push("## Usage"),H.push("");let $=M(Q);H.push(`The root command is directly executable. See [${Q.name}](${$}) for usage details.`),H.push("")}return H.join(`
|
|
3
|
+
`)}function UQ(Q){let Z=[];Z.push("| Command | Type | Documentation |"),Z.push("| ------- | ---- | ------------- |");for(let X of Q){let H=L(X),W=M(X),$=VQ(X);Z.push(`| \`${H}\` | ${$} | [${W}](${W}) |`)}return Z}function VQ(Q){if(Q.runnable&&Q.children.length>0)return"runnable, group";if(Q.runnable)return"runnable";return"group"}function jQ(Q,Z){let X=[],H=L(Q);if(X.push(`# \`${H}\``),X.push(""),Q.description)X.push(Q.description),X.push("");if(X.push("## Usage"),X.push(""),Q.usage)X.push("```"),X.push(Q.usage),X.push("```");else X.push("```"),X.push(r(Q)),X.push("```");if(X.push(""),Q.args.length>0)X.push("## Arguments"),X.push(""),X.push(...l(Q.args)),X.push("");if(Q.flags.length>0)X.push("## Flags"),X.push(""),X.push(...i(Q.flags)),X.push("");return X.push(...o(Q,Z)),X.join(`
|
|
4
|
+
`)}function MQ(Q,Z){let X=[],H=L(Q),W=M(Q);if(X.push(`# \`${H}\``),X.push(""),Q.description)X.push(Q.description),X.push("");if(Q.runnable){if(X.push("## Usage"),X.push(""),Q.usage)X.push("```"),X.push(Q.usage),X.push("```");else X.push("```"),X.push(r(Q)),X.push("```");if(X.push(""),Q.args.length>0)X.push("## Arguments"),X.push(""),X.push(...l(Q.args)),X.push("");if(Q.flags.length>0)X.push("## Flags"),X.push(""),X.push(...i(Q.flags)),X.push("")}X.push("## Subcommands"),X.push("");for(let $ of Q.children){let J=M($),q=A(W,J),R=$.description?` - ${$.description}`:"";X.push(`- [\`${$.name}\`](${q})${R}`)}return X.push(""),X.push(...o(Q,Z)),X.join(`
|
|
5
|
+
`)}function r(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 l(Q){let Z=[];Z.push("| Argument | Type | Required | Description |"),Z.push("| -------- | ---- | -------- | ----------- |");for(let X of Q){let H=X.variadic?`${X.name}...`:X.name,W=X.required?"Yes":"No",$=g(IQ(X));Z.push(`| \`${H}\` | ${X.type} | ${W} | ${$} |`)}return Z}function IQ(Q){let Z=[];if(Q.description)Z.push(Q.description);if(Q.default!==void 0)Z.push(`Default: \`${Q.default}\``);return Z.join(". ")||"-"}function i(Q){let Z=[];Z.push("| Flag | Type | Required | Description |"),Z.push("| ---- | ---- | -------- | ----------- |");for(let X of Q){let H=_Q(X),W=X.required?"Yes":"No",$=g(wQ(X));Z.push(`| ${H} | ${X.type} | ${W} | ${$} |`)}return Z}function _Q(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 wQ(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 o(Q,Z){let X=[],H=M(Q);if(X.push("---"),X.push(""),Q.path.length>1){let $=Q.path.slice(0,-1),J=s(Z,$);if(J){let q=M(J),R=A(H,q),B=L(J);X.push(`Parent: [\`${B}\`](${R})`),X.push("")}}let W=A(H,"SKILL.md");return X.push(`[Skill Overview](${W})`),X.push(""),X}function s(Q,Z){if(FQ(Q.path,Z))return Q;for(let X of Q.children){let H=s(X,Z);if(H)return H}return}function FQ(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 kQ}from"fs/promises";import{join as SQ}from"path";var C="crust.json";async function D(Q){try{let Z=await kQ(SQ(Q,C),"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 e=/^[a-z0-9]+(-[a-z0-9]+)*$/;function QQ(Q){return Q.length>=1&&Q.length<=64&&e.test(Q)}function I(Q){return Q.startsWith("use-")?Q:`use-${Q}`}async function w(Q){let{command:Z,meta:X,agents:H,scope:W="global",clean:$=!0,force:J=!1}=Q,q=I(X.name);if(!QQ(q))throw Error(`Invalid skill name "${q}": must be 1\u201364 lowercase `+`alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens. Pattern: ${e.source}`);let R={...X,name:q},B=p(Z),K=m(B,R),y=PQ(B,R),F=[...K,...y].sort((O,x)=>O.path<x.path?-1:O.path>x.path?1:0),U=[];for(let O of H){let x=S(O,W,R.name),Y=await D(x);if(Y===null){if(await n(x).then(()=>!0).catch(()=>!1)&&!J)throw new j({agent:O,outputDir:x})}let G=Y===null?"installed":Y===R.version?"up-to-date":"updated";if(G==="up-to-date"){U.push({agent:O,outputDir:x,files:[],status:"up-to-date"});continue}let k=G==="updated"?Y??void 0:void 0;if($)await yQ(x);await AQ(x,F),U.push({agent:O,outputDir:x,files:F.map((h)=>h.path),status:G,previousVersion:k})}return{agents:U}}async function N(Q){let{name:Z,agents:X,scope:H="global"}=Q,W=I(Z),$=[];for(let J of X){let q=S(J,H,W);if(await n(q).then(()=>!0).catch(()=>!1))await a(q,{recursive:!0,force:!0}),$.push({agent:J,outputDir:q,status:"removed"});else $.push({agent:J,outputDir:q,status:"not-found"})}return{agents:$}}async function T(Q){let{name:Z,agents:X,scope:H="global"}=Q,W=I(Z),$=[];for(let J of X){let q=S(J,H,W),R=await D(q);$.push({agent:J,outputDir:q,installed:R!==null,version:R??void 0})}return{agents:$}}function PQ(Q,Z){return[{path:C,content:bQ(Q,Z)}]}function bQ(Q,Z){let X=XQ(Q),H={name:Z.name,description:Z.description,version:Z.version,entrypoint:"SKILL.md",commands:X};return`${JSON.stringify(H,null,"\t")}
|
|
6
|
+
`}function XQ(Q){let Z=[Q.path.join(" ")];for(let X of Q.children)Z.push(...XQ(X));return Z}async function yQ(Q){await a(Q,{recursive:!0,force:!0})}async function AQ(Q,Z){let X=new Set;for(let W of Z){let $=t(Q,W.path),J=TQ($);X.add(J)}let H=[...X].sort();for(let W of H)await EQ(W,{recursive:!0});for(let W of Z){let $=t(Q,W.path);await LQ($,W.content,"utf-8")}}import{createCommandNode as CQ,VALIDATION_MODE_ENV as DQ}from"@crustjs/core";import{confirm as NQ,multiselect as vQ,spinner as b}from"@crustjs/prompts";import{bold as v,dim as P,yellow as ZQ}from"@crustjs/style";var HQ="skill",WQ="global";function $Q(Q,Z){return{name:Q.meta.name,description:Q.meta.description??"",version:Z}}async function hQ(Q,Z){let X=Z.scope??WQ,H=await E({scope:X});if(H.length===0)return;let W=$Q(Q,Z.version),J=(await T({name:W.name,agents:H,scope:X})).agents.filter((q)=>q.installed&&q.version!==W.version);if(J.length===0)return;try{await b({message:"Updating skills...",task:async({updateMessage:q})=>{let R=await w({command:Q,meta:W,agents:J.map((K)=>K.agent),scope:Z.scope}),B=R.agents.filter((K)=>K.status==="updated").map((K)=>V[K.agent]);if(B.length>0)q(`Updated skill "${I(W.name)}" to v${W.version} for ${B.join(", ")}`);return R}})}catch(q){if(q instanceof j)console.warn(ZQ(`Skill conflict: "${q.details.outputDir}" already exists but was not created by ${W.name}. Skipping auto-update. Delete or rename the conflicting skill to resolve.`));else throw q}}function uQ(Q){let Z;return{name:"skills",async setup(X,H){Z=X.rootCommand;let W=typeof Q.command==="string"?Q.command:HQ;if(Q.command!==!1)H.addSubCommand(Z,W,pQ(Z,Q));if(process.env[DQ]==="1")return;if(Q.command!==!1&&X.argv[0]===W)return;if(Q.autoUpdate!==!1)await hQ(Z,Q)}}}function pQ(Q,Z){let X=CQ(HQ);return X.meta.description="Manage agent skill installations",X.run=async()=>{let H=$Q(Q,Z.version),W=Z.scope??WQ,$=await E({scope:W});if($.length===0){console.log(ZQ("No supported agents detected. Install Claude Code or OpenCode first."));return}let J=await T({name:H.name,agents:$,scope:W}),q=[],R=J.agents.map((x)=>{let Y=x.installed?`v${x.version} installed`:"not installed";if(x.installed)q.push(x.agent);return{label:V[x.agent],value:x.agent,hint:Y}}),B=process.stdin.isTTY,K=await vQ({message:"Select agents to install skills for",choices:R,default:q,initial:!B?$:void 0,required:!1}),y=K.filter((x)=>!q.includes(x)),F=K.filter((x)=>{let Y=J.agents.find((G)=>G.agent===x);return Y?.installed===!0&&Y.version!==H.version}),U=q.filter((x)=>!K.includes(x)),O=[...y,...F];if(O.length>0)try{let x=await b({message:"Installing skills...",task:async()=>w({command:Q,meta:H,agents:O,scope:W})});console.log(`
|
|
7
|
+
${v(`Installed "${H.name}" v${H.version}`)}`);for(let Y of x.agents)console.log(P(` ${V[Y.agent]} \u2192 ${Y.outputDir}`))}catch(x){if(x instanceof j)if(await NQ({message:`"${x.details.outputDir}" already exists but was not created by Crust. Overwrite?`,default:!1,initial:!B?!1:void 0})){let G=await b({message:"Overwriting skill...",task:async()=>w({command:Q,meta:H,agents:[x.details.agent],scope:W,force:!0})});console.log(`
|
|
8
|
+
${v(`Installed "${H.name}" v${H.version}`)}`);for(let k of G.agents)console.log(P(` ${V[k.agent]} \u2192 ${k.outputDir}`))}else console.log(P(`
|
|
9
|
+
Skipped ${V[x.details.agent]}`));else throw x}if(U.length>0){let Y=(await b({message:"Removing skills...",task:async()=>N({name:H.name,agents:U,scope:W})})).agents.filter((G)=>G.status==="removed").map((G)=>V[G.agent]);if(Y.length>0)console.log(`
|
|
10
|
+
${v(`Removed from ${Y.join(", ")}`)}`)}if(O.length===0&&U.length===0)console.log(P("No changes."))},X}export{N as uninstallSkill,T as skillStatus,uQ as skillPlugin,I as resolveSkillName,QQ as isValidSkillName,w as generateSkill,E as detectInstalledAgents,j 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.11",
|
|
4
4
|
"description": "Agent skill generation from Crust command definitions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,11 +47,11 @@
|
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@crustjs/config": "0.0.0",
|
|
50
|
-
"@crustjs/core": "0.0.
|
|
50
|
+
"@crustjs/core": "0.0.11",
|
|
51
51
|
"bunup": "^0.16.29"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@crustjs/core": "0.0.
|
|
54
|
+
"@crustjs/core": "0.0.11",
|
|
55
55
|
"typescript": "^5"
|
|
56
56
|
}
|
|
57
57
|
}
|