skilless 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin.mjs +3 -3
  2. package/package.json +1 -1
package/dist/bin.mjs CHANGED
@@ -120,6 +120,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
120
120
  `),t=a-1;n=[...e.slice(0,t),`... (content truncated to fit terminal)`].join(`
121
121
  `)}this.#p(()=>this.#a.write(n))===!1&&this.#a.isTTY&&(this.#l=()=>{this.#l=void 0,this.#m()},this.#a.once(`drain`,this.#l)),this.#e=this.#C(n,r)}finally{t&&this.#p(()=>this.#a.write(`\x1B[?2026l`))}return this}start(e){if(e!==void 0&&(this.text=e),this.isSilent)return this;if(!this.isEnabled){let e=this.text?`-`:``,t=` `.repeat(this.#r.indent)+this.#y(e,this.text,this.#r.prefixText,this.#r.suffixText);return t.trim()!==``&&this.#p(()=>this.#a.write(t+`
122
122
  `)),this}return this.isSpinning?this:(this.#r.hideCursor&&Jf.hide(this.#a),this.#r.discardStdin&&o.stdin.isTTY&&(Hp.start(),this.#d=!0),this.#T(),this.render(),this.#o=setInterval(this.render.bind(this),this.interval),this)}stop(){return clearInterval(this.#o),this.#o=void 0,this.#t=-1,this.#n=0,this.#v(),this.#E(),this.#l&&=(this.#a.removeListener(`drain`,this.#l),void 0),this.isEnabled&&(this.clear(),this.#r.hideCursor&&Jf.show(this.#a)),this.#d&&(this.#d=!1,Hp.stop()),this}succeed(e){return this.stopAndPersist({symbol:ip,text:e})}fail(e){return this.stopAndPersist({symbol:op,text:e})}warn(e){return this.stopAndPersist({symbol:ap,text:e})}info(e){return this.stopAndPersist({symbol:rp,text:e})}stopAndPersist(e={}){if(this.isSilent)return this;let t=e.symbol??` `,n=e.text??this.text,r=e.prefixText??this.#r.prefixText,i=e.suffixText??this.#r.suffixText,a=this.#y(t,n,r,i)+`
123
- `;return this.stop(),this.#p(()=>this.#a.write(a)),this}};function Kp(e){return new Gp(e)}var W=te(v(((e,t)=>{let n=process||{},r=n.argv||[],i=n.env||{},a=!(i.NO_COLOR||r.includes(`--no-color`))&&(!!i.FORCE_COLOR||r.includes(`--color`)||n.platform===`win32`||(n.stdout||{}).isTTY&&i.TERM!==`dumb`||!!i.CI),o=(e,t,n=e)=>r=>{let i=``+r,a=i.indexOf(t,e.length);return~a?e+s(i,t,n,a)+t:e+i+t},s=(e,t,n,r)=>{let i=``,a=0;do i+=e.substring(a,r)+n,a=r+t.length,r=e.indexOf(t,a);while(~r);return i+e.substring(a)},c=(e=a)=>{let t=e?o:()=>String;return{isColorSupported:e,reset:t(`\x1B[0m`,`\x1B[0m`),bold:t(`\x1B[1m`,`\x1B[22m`,`\x1B[22m\x1B[1m`),dim:t(`\x1B[2m`,`\x1B[22m`,`\x1B[22m\x1B[2m`),italic:t(`\x1B[3m`,`\x1B[23m`),underline:t(`\x1B[4m`,`\x1B[24m`),inverse:t(`\x1B[7m`,`\x1B[27m`),hidden:t(`\x1B[8m`,`\x1B[28m`),strikethrough:t(`\x1B[9m`,`\x1B[29m`),black:t(`\x1B[30m`,`\x1B[39m`),red:t(`\x1B[31m`,`\x1B[39m`),green:t(`\x1B[32m`,`\x1B[39m`),yellow:t(`\x1B[33m`,`\x1B[39m`),blue:t(`\x1B[34m`,`\x1B[39m`),magenta:t(`\x1B[35m`,`\x1B[39m`),cyan:t(`\x1B[36m`,`\x1B[39m`),white:t(`\x1B[37m`,`\x1B[39m`),gray:t(`\x1B[90m`,`\x1B[39m`),bgBlack:t(`\x1B[40m`,`\x1B[49m`),bgRed:t(`\x1B[41m`,`\x1B[49m`),bgGreen:t(`\x1B[42m`,`\x1B[49m`),bgYellow:t(`\x1B[43m`,`\x1B[49m`),bgBlue:t(`\x1B[44m`,`\x1B[49m`),bgMagenta:t(`\x1B[45m`,`\x1B[49m`),bgCyan:t(`\x1B[46m`,`\x1B[49m`),bgWhite:t(`\x1B[47m`,`\x1B[49m`),blackBright:t(`\x1B[90m`,`\x1B[39m`),redBright:t(`\x1B[91m`,`\x1B[39m`),greenBright:t(`\x1B[92m`,`\x1B[39m`),yellowBright:t(`\x1B[93m`,`\x1B[39m`),blueBright:t(`\x1B[94m`,`\x1B[39m`),magentaBright:t(`\x1B[95m`,`\x1B[39m`),cyanBright:t(`\x1B[96m`,`\x1B[39m`),whiteBright:t(`\x1B[97m`,`\x1B[39m`),bgBlackBright:t(`\x1B[100m`,`\x1B[49m`),bgRedBright:t(`\x1B[101m`,`\x1B[49m`),bgGreenBright:t(`\x1B[102m`,`\x1B[49m`),bgYellowBright:t(`\x1B[103m`,`\x1B[49m`),bgBlueBright:t(`\x1B[104m`,`\x1B[49m`),bgMagentaBright:t(`\x1B[105m`,`\x1B[49m`),bgCyanBright:t(`\x1B[106m`,`\x1B[49m`),bgWhiteBright:t(`\x1B[107m`,`\x1B[49m`)}};t.exports=c(),t.exports.createColors=c}))(),1);const qp=zd.default.prompt;function Jp(){process.stdout.write(`\r\x1B[0J`),process.stdin.isTTY&&process.stdin.setRawMode(!1),Zp()}const Yp={limit:10,up(){if(this.index>0)return this.index--,this.render();if(this.choices[0]?.index!==0)return this.scrollUp()},down(){if(this.index<this.visible.length-1)return this.index++,this.render();if(this.visible.at(-1)?.index!==this.choices.length-1)return this.scrollDown()}};async function Xp(e,t){im();try{return await qp({...e,cancel:Jp})}catch{return Zp(),t}finally{am()}}function Zp(){im(),process.stdout.write(`${W.default.yellow(`!`)} Cancelled\n`),process.exit(1)}async function Qp(e,t=!1){return(await Xp({type:`confirm`,name:`value`,message:e,initial:t},{value:!1})).value}const $p=!!(process.stdin.isTTY&&process.stdout.isTTY);async function em(e,t={}){return(await Xp({type:`input`,name:`value`,message:e,initial:t.initial,validate:t.validate?e=>t.validate((e??``).trim()):void 0},{value:``})).value.trim()}async function tm(e,t){return t.length===0?[]:(await Xp({type:`multiselect`,name:`value`,message:e,choices:t,...Yp},{value:[]})).value}async function nm(e,t,n){let r=n?t.findIndex(e=>e.name===n):-1;return(await Xp({type:`select`,name:`value`,message:e,choices:t,initial:r===-1?0:r,...Yp},{value:``})).value}let G=null;function rm(e){let t=e.step%3+1;return`${e.message}${`.`.repeat(t)}${` `.repeat(3-t)}`}function im(){G?.ora.isSpinning&&G.ora.stop()}function am(){G&&!G.ora.isSpinning&&G.ora.start(rm(G))}async function K(e,t){if(!process.stdout.isTTY||!process.stdout.columns)return t();if(G){let n=G.message;G.message=e;try{return await t()}finally{G&&(G.message=n)}}let n={ora:Kp({stream:process.stdout,color:`cyan`,discardStdin:!1}),message:e,step:0,dots:setInterval(()=>{n.step++,n.ora.text=rm(n)},300)};G=n,am();try{return await t()}finally{clearInterval(n.dots),im(),G=null}}function om(e,t){let n=G?.ora.isSpinning===!0;n&&G.ora.clear(),e.write(t),n&&G.ora.render()}const q={intro(e){om(process.stdout,`${W.default.bgWhite(W.default.black(` skilless `))}${W.default.gray(` v${e}`)}\n\n`)},info(e){om(process.stdout,`${W.default.blue(`·`)} ${e}\n`)},step(e){om(process.stdout,`${W.default.green(`✓`)} ${e}\n`)},warn(e){om(process.stdout,`${W.default.yellow(`!`)} ${e}\n`)},error(e){om(process.stderr,`${W.default.red(`✖`)} ${e}\n`)},dim(e){om(process.stdout,`${W.default.gray(e)}\n`)},blank(){om(process.stdout,`
124
- `)}},sm=Tc({path:j(),contents:j()}),cm=Tc({url:j(),ref:j().optional(),path:j(),hash:j()}),lm=Tc({name:j(),contentHash:j(),editedAt:mc(),updatedAt:mc(),global:N(),source:cm.nullable().default(null)}),um=lm.extend({files:Cc(sm)}),dm=Tc({bound:Cc(j()),unknown:Cc(j())}),fm=Tc({error:j(),message:j().optional()}),pm=new Set([502,503,504]);function mm(e){try{return JSON.parse(e)}catch{return null}}var hm=class{#e;#t;#n=!1;constructor(e,t){this.#e=e,this.#t=t}get baseUrl(){return this.#t}async#r(e,t,n){if(this.#n)throw new rl(this.#t);let r;for(let i=0;i<2;i++){i>0&&await new Promise(e=>setTimeout(e,500));try{let i=await fetch(`${this.#t}/api/v1${t}`,{method:e,headers:{Authorization:`Bearer ${this.#e}`,...n===void 0?{}:{"Content-Type":`application/json`}},body:n===void 0?void 0:JSON.stringify(n),signal:AbortSignal.timeout(1e4)}),a=await i.text();if(pm.has(i.status)){r=Error(`${e} ${t} returned ${i.status}.`);continue}return{status:i.status,text:a}}catch(e){r=e}}throw this.#n=!0,new rl(this.#t,r)}async#i(e,t,n,r){let i=await this.#r(e,t,r);if(i.status===401)throw new tl;if(i.status<200||i.status>=300){let n=fm.safeParse(mm(i.text));throw new P(n.success?n.data.message??n.data.error:`${e} ${t} failed with ${i.status}.`)}if(i.status===204)return n.parse(void 0);let a=n.safeParse(mm(i.text));if(!a.success)throw new P(`Unexpected response from ${e} ${t}.`,{suggestion:`This is likely a version mismatch. Try updating skilless.`});return a.data}async listSkills(){return await this.#i(`GET`,`/skills`,Cc(lm))}async getSkill(e){return await this.#i(`GET`,`/skills/${encodeURIComponent(e)}`,um.nullable())}async putSkill(e,t,n){return await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}`,lm,{files:t,editedAt:n})}async setGlobal(e,t){await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}/global`,yc(),{global:t})}async setSource(e,t){await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}/source`,yc(),{source:t})}async deleteSkill(e){await this.#i(`DELETE`,`/skills/${encodeURIComponent(e)}`,yc())}async getBindings(e){return await this.#i(`GET`,`/projects/skills?key=${encodeURIComponent(e)}`,Cc(um))}async setBindings(e,t){return await this.#i(`PUT`,`/projects/skills`,dm,{key:e,names:t})}};function gm(){return process.env.SKILLESS_TOKEN||cl(Cl,{}).token}function _m(e){ll(Cl,{token:e},384)}function vm(){I(Cl)}function ym(){return(process.env.SKILLESS_API_URL??`https://skilless.dev`).replace(/\/$/,``)}var bm=class{api;#e=null;constructor(){let e=gm();this.api=e?new hm(e,ym()):null}get online(){return this.api!==null&&this.#e===null}async try(e){if(!(!this.api||this.#e))try{return await e(this.api)}catch(e){if(e instanceof rl||e instanceof tl){this.#e=e;return}throw e}}report(){if(this.#e){if(q.blank(),this.#e instanceof rl){q.warn(`Couldn't reach ${this.#e.url}. Run \`skilless sync\` to sync your skills with the cloud.`);return}q.warn(`Your sign-in was rejected, so this only changed this machine.`),q.dim("Run `skilless auth` to sign in again, then `skilless sync` to sync your skills.")}}};function xm(e){return e.includes(`/`)||e.includes(`:`)}function Sm(e){let t=e.trim(),n,r=t.indexOf(`#`);if(r>=0&&(n=t.slice(r+1)||void 0,t=t.slice(0,r)),/^ext::/i.test(t))throw new P(`The ext:: git transport is not supported.`);let i=/^(github|gitlab):(.+)$/.exec(t);i&&(t=i[1]===`gitlab`?`https://gitlab.com/${i[2]}`:i[2]??``);let a;return a=/^https?:\/\//i.test(t)?Cm(t,n):/^[a-z][a-z0-9+.-]*:\/\//i.test(t)||/^[^@/\s]+@[^:/\s]+:/.test(t)?{url:t,ref:n}:wm(t,n),a.subpath&&=Tm(a.subpath),{...a,label:ml(a.url)??a.url}}function Cm(e,t){let n;try{n=new URL(e)}catch{throw new P(`${e} is not a valid URL.`)}let r=n.pathname.split(`/`).filter(Boolean).map(decodeURIComponent),i=`${n.protocol}//${n.host}`;if(n.hostname===`github.com`&&r.length>=2){let[e,n,a,o,...s]=r,c=`${i}/${e}/${n?.replace(/\.git$/,``)}.git`;return(a===`tree`||a===`blob`)&&o?{url:c,ref:o,subpath:s.join(`/`)||void 0}:{url:c,ref:t}}let a=r.indexOf(`-`);return a>0&&r[a+1]===`tree`&&r[a+2]?{url:`${i}/${r.slice(0,a).join(`/`).replace(/\.git$/,``)}.git`,ref:r[a+2],subpath:r.slice(a+3).join(`/`)||void 0}:{url:e,ref:t}}function wm(e,t){let n=/^([^/@\s]+)\/([^/@\s]+)@([^/\s]+)$/.exec(e);if(n)return{url:`https://github.com/${n[1]}/${n[2]}.git`,ref:t,skill:n[3]};let r=/^([^/\s.][^/\s]*)\/([^/\s]+?)(?:\/(.+?))?\/?$/.exec(e);if(!r)throw new P(`Can't tell where ${e} points.`,{suggestion:"Pass `owner/repo` for GitHub, or a full git URL for anywhere else."});return{url:`https://github.com/${r[1]}/${r[2]?.replace(/\.git$/,``)}.git`,ref:t,subpath:r[3]}}function Tm(e){let t=e.replace(/\\/g,`/`).split(`/`).filter(Boolean);if(t.includes(`..`))throw new P(`${e} reaches outside the repository.`);return t.join(`/`)}async function Em(e,t,r){let i=a.mkdtempSync(b.join(s.tmpdir(),`skilless-`));try{let o=[`clone`,`--depth`,`1`,`--quiet`];e.ref&&o.push(`--branch`,e.ref),o.push(`--`,e.url,i);try{await K(`Cloning ${e.label}${e.ref?`#${e.ref}`:``}`,()=>Dm(o,{...process.env,GIT_TERMINAL_PROMPT:`0`,GIT_SSH_COMMAND:process.env.GIT_SSH_COMMAND??`ssh -o BatchMode=yes`}))}catch(t){if(!r.interactive)throw Om(e,t);I(i),a.mkdirSync(i);try{n(`git`,o,{stdio:[`inherit`,`ignore`,`pipe`],encoding:`utf8`})}catch(t){throw Om(e,t)}}return await t(i)}finally{I(i)}}function Dm(e,n){return new Promise((r,i)=>{t(`git`,e,{env:n,encoding:`utf8`},(e,t,n)=>{e?i(Object.assign(e,{stderr:n})):r()})})}function Om(e,t){let n=t.stderr?.trim();return new P(`Couldn't clone ${e.label}.`,{suggestion:n||`Check the address, and that you have access to the repository.`,cause:t})}const km=new Set([`node_modules`,`.git`,`dist`,`build`,`__pycache__`]),Am=[`skills`,`skills/.curated`,`skills/.experimental`,`skills/.system`,`.agents/skills`,`.claude/skills`];function jm(e,t){let n=t?b.join(e,t):e;if(!F(n))throw new P(`${t} does not exist in the repository.`);let r=new Map,i=e=>{let t=Fm(e);r.has(t.name)||r.set(t.name,t)};if(Nm(n))return i(n),[...r.values()];Pm(n,1,i);for(let e of Am)Pm(b.join(n,e),3,i);return r.size===0&&Pm(n,5,i),[...r.values()].sort((e,t)=>e.name.localeCompare(t.name))}function Mm(e,t,n){try{let n=Tm(t);if(Nm(b.join(e,n)))return{dir:b.join(e,n),path:n}}catch{}let r=jm(e).find(e=>e.name===n);return r?{dir:r.dir,path:b.relative(e,r.dir)}:null}function Nm(e){return a.existsSync(b.join(e,zl))}function Pm(e,t,n){let r;try{r=a.readdirSync(e,{withFileTypes:!0})}catch{return}for(let i of r.sort((e,t)=>e.name.localeCompare(t.name))){if(!i.isDirectory()||km.has(i.name))continue;let r=b.join(e,i.name);Nm(r)?n(r):t>1&&Pm(r,t-1,n)}}function Fm(e){let t=Im(a.readFileSync(b.join(e,zl),`utf8`)),n=t.name?.toLowerCase().replace(/[\s_]+/g,`-`);return{name:n&&Ul(n)?n:b.basename(e).toLowerCase(),dir:e,description:t.description}}function Im(e){let t=/^---\r?\n([\s\S]*?)\r?\n---/.exec(e)?.[1];if(!t)return{};let n=e=>{let n=RegExp(`^${e}:[ \\t]*(.+)$`,`m`).exec(t)?.[1]?.trim();if(!(!n||n===`|`||n===`>`))return n.replace(/^(['"])(.*)\1$/,`$2`)};return{name:n(`name`),description:n(`description`)}}var Lm=`skilless`,Rm=`Author skills once, sync them everywhere, install them per project.`;const J=`0.0.2`,zm=Lm,Bm=Rm,Vm=`SKILLESS_TRACE`,Y=Tc({cwd:j()}),X={cwd:new _e(`--cwd <path>`,`The current working directory.`).default(process.cwd()),yes:new _e(`-y, --yes`,`Skip confirmation prompts.`).default(!1),project:new _e(`--project <key>`,`Override the project key. Defaults to the normalized git remote.`)};function Z(e,t){let n=e.safeParse(t);return n.success?n.data:Hm(new P(`Invalid options.`,{suggestion:n.error.issues.map(e=>` ${e.message}`).join(`
125
- `)}))}function Hm(e){process.env[Vm]===`1`?console.trace(e):q.error(W.default.red(e.toString())),process.exit(1)}async function Q(e){try{return await e()}catch(e){return e instanceof P?Hm(e):Hm(new P(e instanceof Error?e.message:String(e),{suggestion:`Run with ${Vm}=1 for a stack trace.`,cause:e}))}}function Um(){let e=gm();if(!e)throw new tl;return new hm(e,ym())}function Wm(e,t){if(t)return t;let n=hl(e);if(!n)throw new nl;return n}const Gm=`~/.agents/skills`;function Km(e){let t=b.normalize(s.homedir());return e===t||e.startsWith(`${t}/`)?`~${e.slice(t.length)}`:e}async function qm(e,t={}){for(let n of e.skipped){if(n.reason===`tracked`){q.warn(`\`${n.name}\` is tracked by git here, so it was left alone.`);continue}let e=(n.occupied?.paths??[]).map(Km).join(` and `);if(!(t.yes||$p&&await Qp(`Overwrite ${e}?`,!1))){$p?q.dim(`Kept ${e}, so ${n.name} is not linked there.`):(q.warn(`Skipped ${n.name}: ${e} was not made by skilless.`),q.dim(` Run this in a terminal to overwrite it, or pass --yes.`));continue}pu(n,{copy:bu(t.copy)}),q.step(`Overwrote ${e}.`)}for(let t of e.missing??[])q.warn(`${t} is not on this machine yet, so it will be linked on the next sync.`)}const Jm=Y.extend({project:j().optional(),copy:N().optional(),global:N().optional(),notGlobal:N().optional(),overwrite:N(),yes:N()}),Ym=new y(`add`).description(`Add skills to this project, from your library or from a git repository.`).argument(`[skills...]`,"Skills to add, or a repo (`owner/repo` or any git URL) then the skills to take from it. Omit the skills to pick from a list.").option(`-g, --global`,`Make these skills global: linked once into ~/.agents/skills and ~/.claude/skills, so every project has them, including cloud agents.`).option(`--not-global`,`Stop treating these skills as global, and unlink them from your user-level skills.`).option(`--copy`,`Write real files instead of symlinking into the store.`).option(`--overwrite`,`From a repo: replace library skills of the same name.`,!1).addOption(X.yes).addOption(X.project).addOption(X.cwd).action(async(e,t)=>{let n=Z(Jm,t);q.intro(J),await Q(async()=>{let t=new bm,[r,...i]=e;if(r&&xm(r)){await Qm(t,Sm(r),i,n);return}let a=e.find(xm);if(a)throw new P(`${a} is a repository, not a skill.`,{suggestion:`Name the repository first: \`skilless add ${a} <skills...>\`.`});let{entries:o,live:s}=await K(`Loading your library`,async()=>(await L(t),R(t))),c=e=>s||e.local!==null,l=e=>{let t=o.filter(t=>e.includes(t.name)&&!c(t)).map(e=>e.name);if(t.length!==0)throw new P(`${t.join(`, `)} ${t.length===1?`is`:`are`} not on this machine, and the server can't be reached to fetch ${t.length===1?`it`:`them`}.`,{suggestion:"Run `skilless sync` once you are back online, then try again."})},u=new Set(o.map(e=>e.name)),d=e.filter(e=>!u.has(e));if(d.length>0)throw new P(`Not in your library: ${d.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});if(n.global||n.notGlobal){let r=n.global===!0,i=e;if(i.length===0){let e=o.filter(e=>e.global!==r&&(!r||c(e)));if(e.length===0){q.info(r?`Every skill in your library is already global.`:`No skills in your library are global.`);return}if(i=await tm(r?`Make global`:`Stop being global`,e.map(e=>({name:e.name}))),i.length===0){q.info(`Nothing selected.`);return}}r&&l(i),await Xm(t,i,r,n),t.report();return}let f=Wm(n.cwd,n.project),p=new Set(await K(`Loading ${f}`,()=>Hu(t,f))),m=e;if(m.length===0){let e=o.filter(e=>!p.has(e.name)&&c(e));if(e.length===0){q.info(s?`Every skill in your library is already in this project.`:`Every skill on this machine is already in this project.`),t.report();return}if(m=await tm(`Add to ${f}`,e.map(e=>({name:e.name}))),m.length===0){q.info(`Nothing selected.`);return}}let h=o.filter(e=>e.global).map(e=>e.name);l(m.filter(e=>!h.includes(e))),await Zm(t,f,m,h,n),t.report()})});async function Xm(e,t,n,r){Mu(t,n),await K(`Saving to skilless.dev`,()=>L(e));for(let e of t)q.step(n?`${e} is now global, so every project has it.`:`${e} is no longer global.`);let i=r.project??hl(r.cwd),a=gu(r.cwd),[o,s]=await K(`Updating your skills`,async()=>{if(!i){let t=await qu(e);return[t,t]}let t=await Wu(e,a,i,{copy:r.copy});return[t,t.user]});for(let e of s.written.filter(e=>t.includes(e)))q.dim(`Linked ${e} into ${Gm}.`);for(let e of s.removed)q.dim(`Unlinked ${e} from ${Gm}.`);if(`project`in o){for(let e of o.project.removed)q.dim(`Moved ${e} out of this project.`);for(let e of o.project.written.filter(e=>t.includes(e)))q.dim(`${e} is still added to this project, so it is linked here again.`)}await qm(o,r)}async function Zm(e,t,n,r,i){for(let e of n)r.includes(e)&&q.dim(`${e} is global, so it is already in this project.`);let a=n.filter(e=>!r.includes(e));Au(t,a);let o=gu(i.cwd),s=await K(`Updating this project`,async()=>(await L(e),Wu(e,o,t,{copy:i.copy})));for(let e of a)q.step(`Added ${e} to this project.`);await qm(s,i)}async function Qm(e,t,n,r){if(r.notGlobal)throw new P(`--not-global only applies to skills already in your library.`);let i=r.global?null:r.project??hl(r.cwd),a=await K(`Loading your library`,async()=>(await L(e),R(e))),o=new Map(a.entries.map(e=>[e.name,e])),s=[...n,...t.skill?[t.skill]:[]],c=await Em(t,async e=>{let n=jm(e,t.subpath);if(n.length===0)throw new P(`No skills found in ${t.label}.`,{suggestion:`A skill is a directory containing a SKILL.md.`});let i=n;if(s.length>0){let e=e=>n.find(t=>t.name===e.toLowerCase()||t.dir.split(`/`).at(-1)?.toLowerCase()===e.toLowerCase()),r=s.filter(t=>!e(t));if(r.length>0)throw new P(`Not in ${t.label}: ${r.join(`, `)}`,{suggestion:`It has: ${n.map(e=>e.name).join(`, `)}`});i=[...new Set(s.map(t=>e(t)))]}else if(n.length>1&&!r.yes){if(!$p)throw new P(`${t.label} has ${n.length} skills.`,{suggestion:`Name the ones you want after the repository, or pass --yes for all of them: ${n.map(e=>e.name).join(`, `)}`});let e=await tm(`Add from ${t.label}`,n.map(e=>({name:e.name,hint:$m(e.description)})));i=n.filter(t=>e.includes(t.name))}let a=[];for(let t of i)try{Wl(t.name),a.push({skill:Kl(t.dir,t.name),path:b.relative(e,t.dir)})}catch(e){q.warn(`Skipped ${t.name}: ${e instanceof P?e.message:e}`)}return a},{interactive:$p});if(c.length===0){q.info(`Nothing selected.`),e.report();return}let l=[],u=[],d=new Map;for(let{skill:e,path:n}of c){d.set(e.name,{url:t.url,...t.ref?{ref:t.ref}:{},path:n,hash:e.contentHash});let i=o.get(e.name),a=i?.local?.contentHash??i?.remote?.contentHash;if(!i||a===e.contentHash){i?zu(e.name,d.get(e.name)):l.push(e),u.push(e.name);continue}if(!(r.overwrite||$p&&!r.yes&&await Qp(`${e.name} is already in your library. Replace it with the one from ${t.label}?`))){q.warn(`Kept your own ${e.name}. Pass --overwrite to replace it.`);continue}l.push(e),u.push(e.name)}let f=await K(`Saving ${l.length} skill(s) to your library`,()=>Ju(e,l,o,d));for(let e of f)q.step(`Copied ${e} into your library from ${t.label}.`);if(u.length===0){e.report();return}r.global?await Xm(e,u,!0,r):i?await Zm(e,i,u,(await K(`Loading your library`,()=>R(e))).entries.filter(e=>e.global).map(e=>e.name),r):(q.blank(),q.dim(`This directory has no git remote, so ${u.length===1?`it is`:`they are`} only in your library. Run \`skilless add ${u[0]}\` inside a project to add ${u.length===1?`it`:`them`} there.`)),e.report()}function $m(e){if(e)return e.length>60?`${e.slice(0,57)}...`:e}function eh(e){switch(process.platform){case`darwin`:return[`open`,[e]];case`win32`:return[`rundll32`,[`url.dll,FileProtocolHandler`,e]];default:return[`xdg-open`,[e]]}}function th(e){let[t,n]=eh(e);try{let e=r(t,n,{stdio:`ignore`,detached:!0,windowsHide:!0});e.on(`error`,()=>{}),e.unref()}catch{}}async function nh(){let e=c.randomBytes(16).toString(`hex`),t=ym(),n=new URL(t).origin,r={"Access-Control-Allow-Origin":n,"Access-Control-Allow-Methods":`POST, OPTIONS`,"Access-Control-Allow-Headers":`Content-Type`,"Access-Control-Allow-Private-Network":`true`},i,a=new Promise((e,t)=>{i={resolve:e,reject:t}}),o=d.createServer((t,n)=>{if(t.method===`OPTIONS`){n.writeHead(204,r),n.end();return}if(t.method!==`POST`){n.writeHead(405,r),n.end();return}let a=``;t.on(`data`,e=>{a+=e}),t.on(`end`,()=>{let t;try{t=JSON.parse(a)}catch{n.writeHead(400,r),n.end();return}if(t.state!==e||!t.token){n.writeHead(400,r),n.end();return}n.writeHead(200,{...r,"Content-Type":`application/json`}),n.end(JSON.stringify({ok:!0})),i.resolve(t.token)})}),s=setTimeout(()=>{i.reject(new P(`Timed out waiting for sign in.`,{suggestion:"Run `skilless auth` again, or paste a token with `skilless auth --token <token>`."}))},3e5);try{await new Promise((e,t)=>{o.once(`error`,t),o.listen(0,`127.0.0.1`,e)});let{port:n}=o.address(),r=`${t}/cli/auth?port=${n}&state=${e}`;q.info(`Opening your browser to sign in…`),q.dim(r),q.blank(),th(r),_m(await K(`Waiting for you to sign in`,()=>a))}finally{clearTimeout(s),o.close()}}const rh=Y.extend({logout:N(),token:j().optional()}),ih=new y(`auth`).description(`Sign in to skilless.dev, or sign out.`).addOption(new _e(`--logout`,`Sign out on this machine.`).default(!1)).addOption(new _e(`--token <token>`,`Save a token directly, for when the browser flow cannot reach localhost.`)).addOption(X.cwd).action(async e=>{let t=Z(rh,e);q.intro(J),await Q(async()=>{if(t.logout){if(!gm()){q.info(`You were not signed in.`);return}vm(),q.step(`Signed out on this machine.`);return}if(t.token){_m(t.token),q.step(`Token saved. You are signed in to skilless.dev.`);return}await nh(),q.step(`Signed in to skilless.dev.`)})}),ah=s.homedir(),oh=process.env.LOCALAPPDATA??b.join(ah,`AppData`,`Local`),sh=process.env.ProgramFiles??`C:/Program Files`,$={cursor:{label:`Cursor`,cli:`cursor`,fallbacks:{darwin:[`/Applications/Cursor.app/Contents/Resources/app/bin/cursor`,`${ah}/Applications/Cursor.app/Contents/Resources/app/bin/cursor`],win32:[b.join(oh,`Programs/cursor/resources/app/bin/cursor.cmd`)],linux:[`/usr/share/cursor/bin/cursor`,`/opt/Cursor/resources/app/bin/cursor`]}},vscode:{label:`VS Code`,cli:`code`,fallbacks:{darwin:[`/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`,`${ah}/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`],win32:[b.join(oh,`Programs/Microsoft VS Code/bin/code.cmd`),b.join(sh,`Microsoft VS Code/bin/code.cmd`)],linux:[`/usr/share/code/bin/code`,`/snap/bin/code`]}},windsurf:{label:`Windsurf`,cli:`windsurf`,fallbacks:{darwin:[`/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf`,`${ah}/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf`],win32:[b.join(oh,`Programs/Windsurf/bin/windsurf.cmd`)],linux:[`/usr/share/windsurf/bin/windsurf`]}},zed:{label:`Zed`,cli:`zed`,fallbacks:{darwin:[`/Applications/Zed.app/Contents/MacOS/cli`,`${ah}/Applications/Zed.app/Contents/MacOS/cli`],win32:[b.join(oh,`Programs/Zed/bin/zed.exe`)],linux:[`${ah}/.local/bin/zed`]}},sublime:{label:`Sublime Text`,cli:`subl`,fallbacks:{darwin:[`/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl`],win32:[b.join(sh,`Sublime Text/subl.exe`)],linux:[`/opt/sublime_text/sublime_text`]}},neovim:{label:`Neovim`,cli:`nvim`,terminal:!0},vim:{label:`Vim`,cli:`vim`,terminal:!0}},ch=Object.keys($);function lh(e){return Object.hasOwn($,e)}function uh(e){let t=i(process.platform===`win32`?`where`:`which`,[e],{encoding:`utf8`,windowsHide:!0});if(t.status!==0||!t.stdout)return null;let n=t.stdout.split(/\r?\n/).map(e=>e.trim()).filter(Boolean);return process.platform===`win32`?n.find(e=>/\.(exe|cmd|bat)$/i.test(e))??n[0]??null:n[0]??null}function dh(e){let t=$[e],n=uh(t.cli);if(n)return n;let r=process.platform;return t.fallbacks?.[r]?.find(e=>F(e))??null}function fh(){return ch.filter(e=>dh(e)!==null)}function ph(e){return $[e].terminal===!0}function mh(e,t){let n=dh(e);if(!n)return Promise.reject(Error(`${$[e].label} was not found. Is \`${$[e].cli}\` on your PATH?`));let i=ph(e);return new Promise((e,a)=>{let o=r(n,[t],{cwd:b.dirname(t),stdio:i?`inherit`:`ignore`,detached:!i,windowsHide:!0,shell:process.platform===`win32`&&/\.(cmd|bat)$/i.test(n)});o.on(`error`,a),i?o.on(`exit`,()=>e()):(o.unref(),setImmediate(e))})}function hh(){let e=cl(Ol,{});return e.editor!==void 0&&!lh(e.editor)&&delete e.editor,e}function gh(e){ll(Ol,e)}function _h(e){let t={...hh(),...e};for(let e of Object.keys(t))t[e]===void 0&&delete t[e];return gh(t),t}const vh=`none`,yh=Tc({unset:N()}),bh=new y(`editor`).description("Choose the editor `skilless create` opens a new skill in.").argument(`[editor]`,`Set it directly: ${ch.join(`, `)}.`).addOption(new _e(`--unset`,`Stop opening new skills in an editor.`).default(!1)).action(async(e,t)=>{let n=Z(yh,t);q.intro(J),await Q(async()=>{let t=hh().editor;if(n.unset){_h({editor:void 0}),q.step(`New skills will no longer open in an editor.`);return}if(e){if(!lh(e))throw new P(`${e} is not an editor skilless knows.`,{suggestion:`Pick one of: ${ch.join(`, `)}.`});_h({editor:e}),q.step(`New skills will open in ${$[e].label}.`);return}if(!$p){q.info(t?`Editor: ${$[t].label}`:`No editor set.`);return}let r=fh();if(r.length===0)throw new P(`None of the editors skilless knows are installed.`,{suggestion:`skilless looks for: ${ch.map(e=>$[e].cli).join(`, `)}.`});let i=await nm(`Open new skills in`,[...r.map(e=>({name:e,message:$[e].label,hint:e===t?W.default.gray(`current`):void 0})),{name:vh,message:`None`,hint:t?void 0:W.default.gray(`current`)}],t??vh);if(i===vh){_h({editor:void 0}),q.step(`New skills will not open in an editor.`);return}lh(i)&&(_h({editor:i}),q.step(`New skills will open in ${$[i].label}.`))})}),xh=new y(`config`).description(`View or change your skilless settings.`).addCommand(bh).action(async()=>{q.intro(J),await Q(async()=>{let e=hh();q.info(`editor ${e.editor?$[e.editor].label:W.default.gray(`not set`)}`),q.blank(),q.dim(`Stored in ${Ol}`)})}),Sh=Y.extend({description:j().optional(),project:j().optional(),global:N().optional(),open:N()}),Ch=new y(`create`).description(`Create a skill, add it to this project, and print the path to edit it.`).argument(`[name]`,`The name of the skill. You will be asked if you leave it out.`).option(`-d, --description <description>`,`What the skill does, for the frontmatter.`).option(`-g, --global`,`Make it global: linked once into ~/.agents/skills and ~/.claude/skills, so every project has it, including cloud agents.`).option(`--no-open`,"Do not open SKILL.md in your editor (see `skilless config editor`).").addOption(X.project).addOption(X.cwd).action(async(e,t)=>{let n=Z(Sh,t);q.intro(J),await Q(async()=>{let t=new bm,r=await K(`Loading your library`,async()=>(await L(t),R(t))),i=new Set(r.entries.map(e=>e.name)),a=e;if(!a){if(!$p)throw new P(`No name given.`,{suggestion:"Run `skilless create <name>`."});a=await em(`Name`,{validate:e=>e?Ul(e)?i.has(e)?`${e} already exists in your library.`:!0:`Use lowercase letters, digits, dots, dashes and underscores.`:`A name is required.`})}if(Wl(a),Xl(a))throw new P(`${a} already exists in your library.`,{suggestion:`Edit it at ${b.join(Al(a),`SKILL.md`)}`});if(i.has(a))throw new P(`${a} already exists in your library.`,{suggestion:`Run \`skilless sync\` to bring ${a} down to this machine.`});let o=n.description??await em(`What does this skill do?`);ql(a,Zl(a,o));let s=Kl(Al(a),a);if(await K(`Creating ${a}`,()=>t.try(e=>e.putSkill(a,s.files,s.editedAt)))){let e=vu();e.skills[a]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:Date.now()},yu(e)}q.step(`Created ${a} in your library.`),n.global&&Mu([a],!0);let c=n.project??hl(n.cwd);if(c){n.global||Au(c,[a]);let e=gu(n.cwd);await qm(await K(`Updating this project`,async()=>(await L(t),Wu(t,e,c))))}else n.global?await qm(await K(`Linking your global skills`,async()=>(await L(t),qu(t)))):await K(`Saving to skilless.dev`,()=>L(t));n.global?q.step(`${a} is now global, so every project has it.`):c?q.step(`Added ${a} to ${c}.`):q.warn(`No git remote here, so ${a} was not added to a project.`);let l=b.join(Al(a),`SKILL.md`),u=n.open?hh().editor:void 0,d=u&&(!ph(u)||$p);if(q.blank(),u&&d)try{q.dim(`Opening ${a} in ${$[u].label}...`),await mh(u,l)}catch(e){q.warn(e instanceof Error?e.message:String(e)),q.dim(`Edit ${a} at ${l}`)}else q.dim(`Edit ${a} at ${l}`);t.api&&q.dim("Run `skilless sync` to save your changes."),t.report()})}),wh=Y.extend({yes:N()}),Th=new y(`delete`).description(`Delete skills from your library, everywhere.`).argument(`<skills...>`,`Skills to delete.`).addOption(X.yes).addOption(X.cwd).action(async(e,t)=>{let n=Z(wh,t);q.intro(J),await Q(async()=>{let t=new bm,r=await K(`Loading your library`,async()=>(await L(t),R(t))),i=new Set(r.entries.map(e=>e.name)),a=e.filter(e=>!i.has(e));if(a.length>0)throw new P(`Not in your library: ${a.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});if(q.warn(`This deletes ${e.join(`, `)} from every project, on every machine.`),q.dim(`Run \`skilless remove ${e.join(` `)}\` to take ${e.length===1?`it`:`them`} out of just this project.`),!(n.yes||await Qp(`Delete ${e.join(`, `)}?`,!1))){q.info(`Nothing was deleted.`);return}Pu(e),Bu(e);let o=vu(),s=gu(n.cwd);for(let t of e)I(Al(t)),delete o.skills[t],q.step(`Deleted ${t} from your library.`);yu(o),mu(s,e),mu($l(),e),await K(`Deleting from skilless.dev`,()=>L(t)),q.blank(),q.dim("Other checkouts will clean up on their next `skilless install`."),q.dim(`Restore at skilless.dev/skills within 30 days.`),t.report()})}),Eh=Y.extend({yes:N(),overwrite:N()});function Dh(e){return a.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(t=>({name:t.name,dir:b.join(e,t.name)})).filter(e=>F(b.join(e.dir,zl))).sort((e,t)=>e.name.localeCompare(t.name))}const Oh=new y(`import`).description(`Import a directory of existing skills into your library.`).argument(`<dir>`,`A directory whose subdirectories each contain a SKILL.md.`).option(`--overwrite`,`Replace skills that already exist.`,!1).addOption(X.yes).addOption(X.cwd).action(async(e,t)=>{let n=Z(Eh,t);q.intro(J),await Q(async()=>{let t=new bm,r=b.resolve(n.cwd,e);if(!F(r))throw new P(`${r} does not exist.`);let i=Dh(r);if(i.length===0)throw new P(`No skills found in ${r}.`,{suggestion:`A skill is a directory containing a ${zl}.`});let a=await K(`Loading your library`,async()=>(await L(t),R(t))),o=new Map(a.entries.map(e=>[e.name,e])),s=[],c=[];for(let e of i){if(!Ul(e.name)){c.push({name:e.name,reason:`invalid name`});continue}if(o.has(e.name)&&!n.overwrite){c.push({name:e.name,reason:`already in your library`});continue}try{s.push(Kl(e.dir,e.name))}catch(t){c.push({name:e.name,reason:t instanceof Error?t.message:`unreadable`})}}for(let e of c)q.warn(`Skipped ${e.name}: ${e.reason}.`);if(s.length===0){q.info(`Nothing to import.`),t.report();return}if(q.blank(),q.info(`Importing: ${s.map(e=>e.name).join(`, `)}`),!(n.yes||await Qp(`Import ${s.length} skill(s)?`,!0)))return;let l=await K(`Importing ${s.length} skill(s)`,()=>Ju(t,s,o));for(let e of l)q.step(`Imported ${e} into your library.`);q.blank(),q.dim(`Run \`skilless add ${s[0]?.name??`<skill>`}\` to add one to this project.`),t.report()})}),kh=new y(`init`).description(`Create the local skilless directory.`).addOption(X.cwd).action(async e=>{Z(Y,e),q.intro(J),await Q(async()=>{let e=!F(Sl);al(Sl),q.step(`${e?`Created`:`Found`} your library at ${xl}.`),q.blank(),q.dim("Run `skilless create <name>` to make a skill."),q.dim("Run `skilless import <dir>` to bring in skills you already have."),gm()||q.dim("Run `skilless auth` to sync your skills with the cloud.")})}),Ah=Y.extend({project:j().optional(),copy:N().optional()}),jh=new y(`install`).description(`Install every skill added to this project, and your global skills at the user level.`).option(`--copy`,`Write real files instead of symlinking into the store.`).addOption(X.project).addOption(X.cwd).action(async e=>{let t=Z(Ah,e);q.intro(J),await Q(async()=>{let e=Um(),n=Wm(t.cwd,t.project),r=gu(t.cwd),i=await K(`Installing ${n}'s skills`,async()=>(await Fu(e),Cu(e,r,n,{copy:t.copy}))),{project:a,user:o}=i;for(let e of[...a.pruned,...o.pruned])q.dim(`Pruned ${e}, which is no longer in your library.`);for(let e of[...a.fetched,...o.fetched])q.dim(`Fetched ${e} from your library.`);for(let e of a.removed)q.dim(`Removed ${e}, which is no longer in this project.`);for(let e of o.removed)q.dim(`Removed ${e} from ${Gm}, since it is no longer global.`);for(let e of a.written)q.step(`Installed ${e}.`);for(let e of o.written)q.step(`Installed ${e} globally.`);if(i.count===0){q.info(`No skills added to ${n} yet.`),q.dim("Run `skilless add <skill>` to add one.");return}await qm(i,{copy:t.copy}),q.blank(),q.dim(bu(t.copy)?`Wrote real files into .agents/skills, and global skills into ~/.agents/skills.`:`Linked .agents/skills, and global skills in ~/.agents/skills, to your library.`)})}),Mh=Y.extend({project:Dc([N(),j()]).optional()});function Nh(e,t){let n=[];return e.global&&n.push(`global`),e.source&&n.push(`from ${ml(e.source.url)??e.source.url}`),e.local?t&&e.unsynced&&n.push(`not synced`):n.push(`not on this machine`),n.map(e=>` · ${e}`).join(``)}const Ph=new y(`list`).description(`List your skills, or the ones added to this project.`).option(`-p, --project [key]`,`List this project’s skills instead of your library.`).addOption(X.cwd).action(async e=>{let t=Z(Mh,e);q.intro(J),await Q(async()=>{let e=new bm,n=await K(`Loading your library`,async()=>(await L(e),R(e)));if(t.project!==void 0){let r=Wm(t.cwd,typeof t.project==`string`?t.project:void 0),i=Uu(n,await K(`Loading ${r}`,()=>Hu(e,r)));q.info(`${W.default.bold(r)}`),q.blank(),i.length===0&&q.dim("No skills in this project. Run `skilless add <skill>` to add one.");for(let t of i)q.dim(` ${t.name}${W.default.gray(Nh(t,e.api!==null))}`);e.report();return}n.entries.length===0&&q.dim("Your library is empty. Run `skilless create <name>` to make one.");for(let t of n.entries){let n=t.local?.editedAt??t.remote?.updatedAt??0,r=new Date(n).toISOString().slice(0,10);q.dim(` ${t.name.padEnd(28)} ${W.default.gray(r+Nh(t,e.api!==null))}`)}e.report()})}),Fh=Y.extend({yes:N(),user:N().optional()});function Ih(e,t){return F(e)?a.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.isSymbolicLink()).map(n=>({name:n.name,dir:b.join(e,n.name),scope:t})).filter(e=>F(b.join(e.dir,zl))):[]}const Lh=new y(`migrate`).description(`Move this project's skills into your library and link them back. Offers your user-level skills too, as globals linked back at the user level.`).option(`--user`,`Migrate your user-level skills too, without asking.`).addOption(X.yes).addOption(X.cwd).action(async e=>{let t=Z(Fh,e);q.intro(J),await Q(async()=>{let e=new bm,n=gu(t.cwd),r=hl(t.cwd),i=r?[jl,Ml].flatMap(e=>Ih(b.join(n,e),`project`).filter(e=>{let t=vl(n,b.relative(n,e.dir));return t&&q.warn(`${e.name} is committed to this repo, so it was left alone. Remove it from git first to migrate it.`),!t})):[],a=[...Ih(Fl(),`user`),...Ih(Pl(),`user`)];if(r||q.dim(`No git remote here, so there are no project skills to migrate.`),i.length===0&&a.length===0){q.info(`Nothing to migrate.`);return}let o=await K(`Loading your library`,async()=>(await L(e),R(e))),s=new Map(o.entries.map(e=>[e.name,e])),c=async e=>{let t=new Map;for(let n of e)t.set(n.name,[...t.get(n.name)??[],n]);let n=[];for(let[e,r]of[...t].sort(([e],[t])=>e.localeCompare(t))){if(!Ul(e)){q.warn(`Skipped ${e}: not a valid skill name.`);continue}let t;try{t=r.map(t=>Kl(t.dir,e))}catch(t){q.warn(`Skipped ${e}: ${t instanceof Error?t.message:`unreadable`}.`);continue}let i=t[0];if(!i)continue;if(t.some(e=>e.contentHash!==i.contentHash)){q.warn(`Skipped ${e}: found more than one version (${r.map(e=>e.dir).join(`, `)}).`);continue}let a=s.get(e),o=a?.local?.contentHash??a?.remote?.contentHash;if(o&&o!==i.contentHash){q.warn(`Skipped ${e}: a different ${e} is already in your library. Rename one to migrate it.`);continue}n.push({skill:i,dirs:r.map(e=>e.dir),scope:r.some(e=>e.scope===`user`)?`user`:`project`,adopted:o!==void 0})}return n},l=await c(i);if(l.length>0&&(q.info(`Project skills in ${r}: ${l.map(e=>e.skill.name).join(`, `)}`),t.yes||await Qp(`Move ${l.length} skill(s) into skilless and link them here?`,!0)||(l=[])),a.length>0){let e=[...new Set(a.map(e=>e.name))].sort();if(q.info(`User-level skills: ${e.join(`, `)}`),t.user||!t.yes&&$p&&await Qp(`Would you also like to migrate your user-level skills? They become global and are linked back into ~/.agents/skills, so every project still gets them.`,!1)){let e=new Set(l.map(e=>e.skill.name)),t=await c([...a,...i.filter(e=>a.some(t=>t.name===e.name))]);l=[...l.filter(e=>!t.some(t=>t.skill.name===e.skill.name)),...t];for(let t of e)l.some(e=>e.skill.name===t)||q.warn(`Skipped ${t}: its project and user-level copies differ.`)}}if(l.length===0){q.info(`Nothing was migrated.`);return}let u=vu();for(let{skill:t,adopted:n}of l)ql(t.name,t.files,t.editedAt),!n&&(await K(`Saving ${t.name} to your library`,()=>e.try(e=>e.putSkill(t.name,t.files,t.editedAt)))?u.skills[t.name]={contentHash:t.contentHash,editedAt:t.editedAt,syncedAt:Date.now()}:delete u.skills[t.name]);yu(u);for(let{dirs:e}of l)for(let t of e)I(t);let d=l.filter(e=>e.scope===`user`).map(e=>e.skill.name),f=l.filter(e=>e.scope===`project`).map(e=>e.skill.name);d.length>0&&Mu(d,!0),r&&f.length>0&&Au(r,f),await K(`Saving to skilless.dev`,()=>L(e));for(let e of f)q.step(`Migrated ${e} into your library and this project.`);for(let e of d)q.step(`Migrated ${e} into your library as a global skill.`);r?await qm(await K(`Updating this project`,()=>Wu(e,n,r)),{yes:t.yes}):d.length>0&&await qm(await K(`Linking your global skills`,()=>qu(e)),{yes:t.yes}),e.report()})}),Rh=Y.extend({project:j().optional(),copy:N().optional()}),zh=new y(`remove`).description(`Remove skills from this project. They stay in your library.`).argument(`<skills...>`,`Skills to remove.`).option(`--copy`,`Write real files instead of symlinking into the store.`).addOption(X.project).addOption(X.cwd).action(async(e,t)=>{let n=Z(Rh,t);q.intro(J),await Q(async()=>{let t=new bm,r=Wm(n.cwd,n.project),[i,a]=await K(`Loading your library`,async()=>(await L(t),[await R(t),new Set(await Hu(t,r))])),o=new Set(i.entries.filter(e=>e.global).map(e=>e.name)),s=[];for(let t of e){if(o.has(t)){q.warn(`${t} is global, so it is in every project.`),q.dim(` Run \`skilless add --not-global ${t}\` to take it out of every project.`);continue}if(!a.has(t)){q.warn(`${t} is not in this project.`);continue}s.push(t)}if(s.length===0){t.report();return}ju(r,s);let c=gu(n.cwd),l=await K(`Updating this project`,async()=>(await L(t),Wu(t,c,r,{copy:n.copy})));for(let e of s)q.step(`Removed ${e} from this project.`);await qm(l,{copy:n.copy}),q.blank(),q.dim(`Run \`skilless delete ${s.join(` `)}\` to delete ${s.length===1?`it`:`them`}.`),t.report()})});function Bh(e,t,n,r){let i=[...new Set([...e.keys(),...t.keys()])].sort(),a=[];for(let o of i){let i=e.get(o),s=t.get(o),c=n.skills[o];if(i&&s){if(i.contentHash===s.contentHash){a.push({type:`noop`,name:o});continue}let e=!c||c.contentHash!==i.contentHash,t=!c||c.contentHash!==s.contentHash;if(e&&!t)a.push({type:`push`,name:o});else if(!e&&t)a.push({type:`pull`,name:o});else{let e=r??(i.editedAt>=s.editedAt?`push`:`pull`);a.push({type:e===`push`?`conflict-push`:`conflict-pull`,name:o})}continue}if(i&&!s){a.push({type:c?`delete-local`:`push`,name:o});continue}!i&&s&&a.push({type:c?`delete-remote`:`pull`,name:o})}return a}function Vh(e,t,n){return Hh(e,t===`push`?`remote`:`local`,n)}function Hh(e,t,n){let r=new Date().toISOString().replace(/[:.]/g,`-`),i=b.join(kl,e,`${r}-${t}`);for(let e of n)sl(b.join(i,e.path),e.contents);return i}async function Uh(e,t,n){let r={actions:[],conflicts:[],skipped:[]},i=Date.now();for(let a of t){let{name:t}=a,o=n.local.get(t);switch(a.type){case`noop`:o&&(n.state.skills[t]={contentHash:o.contentHash,editedAt:o.editedAt,syncedAt:i});break;case`push`:case`conflict-push`:if(!o)break;if(a.type===`conflict-push`){let n=await e.getSkill(t);n&&r.conflicts.push({name:t,kept:`push`,stashedAt:Vh(t,`push`,n.files)})}await e.putSkill(t,o.files,o.editedAt),n.state.skills[t]={contentHash:o.contentHash,editedAt:o.editedAt,syncedAt:i};break;case`pull`:case`conflict-pull`:{let s=await e.getSkill(t);if(!s)break;a.type===`conflict-pull`&&o&&r.conflicts.push({name:t,kept:`pull`,stashedAt:Vh(t,`pull`,o.files)}),ql(t,s.files,s.editedAt),n.state.skills[t]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:i};break}case`delete-local`:if(!(n.yes||await Qp(`Delete ${t} from this machine? It was deleted elsewhere.`,!0))){r.skipped.push(t);continue}I(Al(t)),delete n.state.skills[t];break;case`delete-remote`:if(!(n.yes||await Qp(`Delete ${t} everywhere? It was deleted on this machine.`,!0))){r.skipped.push(t);continue}await e.deleteSkill(t),delete n.state.skills[t];break}r.actions.push(a)}return r}function Wh(e){switch(e.type){case`push`:return`Pushed ${e.name} to skilless.dev.`;case`pull`:return`Pulled ${e.name} from skilless.dev.`;case`conflict-push`:return`${e.name} changed in both places. Kept the copy on this machine.`;case`conflict-pull`:return`${e.name} changed in both places. Kept the copy from skilless.dev.`;case`delete-local`:return`Deleted ${e.name} from this machine.`;case`delete-remote`:return`Deleted ${e.name} everywhere.`;default:return null}}const Gh=Y.extend({yes:N(),push:N().optional(),pull:N().optional()}),Kh=new y(`sync`).description(`Sync your library with skilless.dev, in both directions, and update this project's skills and your global ones.`).option(`--push`,`On a conflict, keep the local copy.`).option(`--pull`,`On a conflict, keep the remote copy.`).addOption(X.yes).addOption(X.cwd).action(async e=>{let t=Z(Gh,e);q.intro(J),await Q(async()=>{let e=Um(),[n,r]=await K(`Checking skilless.dev`,async()=>[await Fu(e),await e.listSkills()]),i=new Map(Yl().map(e=>[e.name,e])),a=new Map(r.map(e=>[e.name,e])),o=vu(),s=Bh(i,a,o,t.push?`push`:t.pull?`pull`:void 0),c=await K(`Syncing your library`,()=>Uh(e,s,{local:i,state:o,yes:t.yes}));yu(o);let l=c.actions.map(Wh).filter(e=>e!==null);for(let e of l)q.step(e);let[u,d]=await K(`Syncing your library`,async()=>{let t=n+await Fu(e);return[t,l.length>0||t>0?await e.listSkills():r]});u>0&&q.step(`Sent ${u} change(s) made while offline.`),Ll(d),Vu(d);let f=hl(t.cwd),p=await K(f?`Updating this project`:`Updating your global skills`,async()=>{if(!f)return{user:Tu(d.filter(e=>e.global).map(e=>e.name))};let n=await Cu(e,gu(t.cwd),f);return{...n,project:n.project}}),m=`project`in p?p.project:void 0,{user:h}=p;if(m){for(let e of m.added)q.step(`Linked ${e} into this project.`);for(let e of m.removed)q.step(`Unlinked ${e}, which is no longer in this project.`);for(let e of m.pruned)q.step(`Unlinked ${e}, which is no longer in your library.`)}for(let e of h.added)q.step(`Linked ${e} into ${Gm}.`);for(let e of h.removed)q.step(`Unlinked ${e} from ${Gm}, since it is no longer global.`);for(let e of h.pruned)q.step(`Unlinked ${e} from ${Gm}, since it is no longer in your library.`);let g=[m,h].reduce((e,t)=>e+(t?t.added.length+t.removed.length+t.pruned.length:0),0);if(l.length===0&&g===0&&u===0&&q.info(`Everything is already in sync.`),c.conflicts.length>0){q.blank(),q.warn(`${c.conflicts.length} skill(s) changed in two places. The copy that lost is kept here:`);for(let e of c.conflicts)q.dim(` ${e.stashedAt}`)}await qm({skipped:[...m?.skipped??[],...h.skipped]},{yes:t.yes}),c.skipped.length>0&&(q.blank(),q.dim(`Skipped ${c.skipped.join(`, `)}.`))})}),qh=Y.extend({yes:N(),force:N()});function Jh(e){let t=ml(e.url)??e.url;return e.ref?`${t}#${e.ref}`:t}const Yh=new y(`update`).description(`Update skills added from a git repository to what the repository has now.`).argument(`[skills...]`,`Skills to update. Omit to update every skill added from a repository.`).option(`-f, --force`,`Replace skills you have edited too. Your copy is kept first.`,!1).addOption(X.yes).addOption(X.cwd).action(async(e,t)=>{let n=Z(qh,t);q.intro(J),await Q(async()=>{let t=new bm,r=await K(`Loading your library`,async()=>(await L(t),R(t))),i=new Map(r.entries.map(e=>[e.name,e])),a=r.entries.filter(e=>e.source!==null);if(e.length>0){let t=e.filter(e=>!i.has(e));if(t.length>0)throw new P(`Not in your library: ${t.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});let n=e.filter(e=>!i.get(e)?.source);if(n.length>0)throw new P(`${n.join(`, `)} ${n.length===1?`was`:`were`} not added from a repository, so there is nothing to update from.`)}let o=e.length>0?a.filter(t=>e.includes(t.name)):a;if(o.length===0){q.info(`None of your skills were added from a repository.`),q.dim("Run `skilless add owner/repo` to add some."),t.report();return}let s=new Map;for(let e of o){let t=`${e.source.url}#${e.source.ref??``}`;s.set(t,[...s.get(t)??[],e])}let c=[],l=new Map,u=[],d=[],f=[],p=[];for(let e of s.values()){let r=e[0].source,i=Jh(r);await Em({url:r.url,ref:r.ref,label:i},async r=>{for(let a of e){let e=Mm(r,a.source.path,a.name);if(!e){q.warn(`${a.name} is no longer in ${i}.`);continue}let o;try{o=Kl(e.dir,a.name)}catch(e){q.warn(`Skipped ${a.name}: ${e instanceof Error?e.message:e}`);continue}let s={...a.source,path:e.path,hash:o.contentHash},p=a.local?.contentHash??a.remote?.contentHash;if(o.contentHash===a.source.hash||p===o.contentHash){zu(a.name,s),u.push(a.name);continue}if(p!==a.source.hash){if(!(n.force||$p&&!n.yes&&await Qp(`You have edited ${a.name} since it came from ${i}. Replace your edits with what it has now?`))){d.push(a.name);continue}let e=a.local?.files??(await K(`Fetching your copy of ${a.name}`,()=>t.try(e=>e.getSkill(a.name))))?.files;if(!e){q.warn(`Couldn't fetch your copy of ${a.name} to keep it, so it was left alone.`);continue}f.push({name:a.name,dir:Hh(a.name,`update`,e)})}c.push(o),l.set(a.name,s)}},{interactive:$p}).then(()=>!0,e=>{if(!(e instanceof P))throw e;return q.warn(e.toString()),!1})||p.push(...e.map(e=>e.name))}q.blank();let m=await K(`Saving ${c.length} skill(s) to your library`,()=>Ju(t,c,i,l));for(let e of m)q.step(`Updated ${e}.`);if(u.length>0&&q.info(u.length===1?`${u[0]} is already up to date.`:`${u.length} skills are already up to date.`),d.length>0&&q.warn(`Kept your edits to ${d.join(`, `)}. Run \`skilless update ${d.join(` `)} --force\` to replace them.`),p.length>0&&q.warn(`Couldn't check ${p.join(`, `)}.`),f.length>0){q.blank(),q.dim(`Your edited copies are kept here:`);for(let e of f)q.dim(` ${e.dir}`)}t.report()})});le.name(zm).description(Bm).version(J).addCommand(kh).addCommand(ih).addCommand(Ch).addCommand(Ym).addCommand(Yh).addCommand(zh).addCommand(Th).addCommand(jh).addCommand(Kh).addCommand(Oh).addCommand(Lh).addCommand(Ph).addCommand(xh).parse();export{};
123
+ `;return this.stop(),this.#p(()=>this.#a.write(a)),this}};function Kp(e){return new Gp(e)}var W=te(v(((e,t)=>{let n=process||{},r=n.argv||[],i=n.env||{},a=!(i.NO_COLOR||r.includes(`--no-color`))&&(!!i.FORCE_COLOR||r.includes(`--color`)||n.platform===`win32`||(n.stdout||{}).isTTY&&i.TERM!==`dumb`||!!i.CI),o=(e,t,n=e)=>r=>{let i=``+r,a=i.indexOf(t,e.length);return~a?e+s(i,t,n,a)+t:e+i+t},s=(e,t,n,r)=>{let i=``,a=0;do i+=e.substring(a,r)+n,a=r+t.length,r=e.indexOf(t,a);while(~r);return i+e.substring(a)},c=(e=a)=>{let t=e?o:()=>String;return{isColorSupported:e,reset:t(`\x1B[0m`,`\x1B[0m`),bold:t(`\x1B[1m`,`\x1B[22m`,`\x1B[22m\x1B[1m`),dim:t(`\x1B[2m`,`\x1B[22m`,`\x1B[22m\x1B[2m`),italic:t(`\x1B[3m`,`\x1B[23m`),underline:t(`\x1B[4m`,`\x1B[24m`),inverse:t(`\x1B[7m`,`\x1B[27m`),hidden:t(`\x1B[8m`,`\x1B[28m`),strikethrough:t(`\x1B[9m`,`\x1B[29m`),black:t(`\x1B[30m`,`\x1B[39m`),red:t(`\x1B[31m`,`\x1B[39m`),green:t(`\x1B[32m`,`\x1B[39m`),yellow:t(`\x1B[33m`,`\x1B[39m`),blue:t(`\x1B[34m`,`\x1B[39m`),magenta:t(`\x1B[35m`,`\x1B[39m`),cyan:t(`\x1B[36m`,`\x1B[39m`),white:t(`\x1B[37m`,`\x1B[39m`),gray:t(`\x1B[90m`,`\x1B[39m`),bgBlack:t(`\x1B[40m`,`\x1B[49m`),bgRed:t(`\x1B[41m`,`\x1B[49m`),bgGreen:t(`\x1B[42m`,`\x1B[49m`),bgYellow:t(`\x1B[43m`,`\x1B[49m`),bgBlue:t(`\x1B[44m`,`\x1B[49m`),bgMagenta:t(`\x1B[45m`,`\x1B[49m`),bgCyan:t(`\x1B[46m`,`\x1B[49m`),bgWhite:t(`\x1B[47m`,`\x1B[49m`),blackBright:t(`\x1B[90m`,`\x1B[39m`),redBright:t(`\x1B[91m`,`\x1B[39m`),greenBright:t(`\x1B[92m`,`\x1B[39m`),yellowBright:t(`\x1B[93m`,`\x1B[39m`),blueBright:t(`\x1B[94m`,`\x1B[39m`),magentaBright:t(`\x1B[95m`,`\x1B[39m`),cyanBright:t(`\x1B[96m`,`\x1B[39m`),whiteBright:t(`\x1B[97m`,`\x1B[39m`),bgBlackBright:t(`\x1B[100m`,`\x1B[49m`),bgRedBright:t(`\x1B[101m`,`\x1B[49m`),bgGreenBright:t(`\x1B[102m`,`\x1B[49m`),bgYellowBright:t(`\x1B[103m`,`\x1B[49m`),bgBlueBright:t(`\x1B[104m`,`\x1B[49m`),bgMagentaBright:t(`\x1B[105m`,`\x1B[49m`),bgCyanBright:t(`\x1B[106m`,`\x1B[49m`),bgWhiteBright:t(`\x1B[107m`,`\x1B[49m`)}};t.exports=c(),t.exports.createColors=c}))(),1);const qp=zd.default.prompt;function Jp(){process.stdout.write(`\r\x1B[0J`),process.stdin.isTTY&&process.stdin.setRawMode(!1),Zp()}const Yp={limit:10,up(){if(this.index>0)return this.index--,this.render();if(this.choices[0]?.index!==0)return this.scrollUp()},down(){if(this.index<this.visible.length-1)return this.index++,this.render();if(this.visible.at(-1)?.index!==this.choices.length-1)return this.scrollDown()}};async function Xp(e,t){im();try{return await qp({...e,cancel:Jp})}catch{return Zp(),t}finally{am()}}function Zp(){im(),process.stdout.write(`${W.default.yellow(`!`)} Cancelled\n`),process.exit(1)}async function Qp(e,t=!1){return(await Xp({type:`confirm`,name:`value`,message:e,initial:t},{value:!1})).value}const $p=!!(process.stdin.isTTY&&process.stdout.isTTY);async function em(e,t={}){return(await Xp({type:`input`,name:`value`,message:e,initial:t.initial,validate:t.validate?e=>t.validate((e??``).trim()):void 0},{value:``})).value.trim()}async function tm(e,t){return t.length===0?[]:(await Xp({type:`multiselect`,name:`value`,message:e,choices:t,...Yp},{value:[]})).value}async function nm(e,t,n){let r=n?t.findIndex(e=>e.name===n):-1;return(await Xp({type:`select`,name:`value`,message:e,choices:t,initial:r===-1?0:r,...Yp},{value:``})).value}let G=null;function rm(e){let t=e.step%3+1;return`${e.message}${`.`.repeat(t)}${` `.repeat(3-t)}`}function im(){G?.ora.isSpinning&&G.ora.stop()}function am(){G&&!G.ora.isSpinning&&G.ora.start(rm(G))}async function K(e,t){if(!process.stdout.isTTY||!process.stdout.columns)return t();if(G){let n=G.message;G.message=e;try{return await t()}finally{G&&(G.message=n)}}let n={ora:Kp({stream:process.stdout,color:`cyan`,discardStdin:!1}),message:e,step:0,dots:setInterval(()=>{n.step++,n.ora.text=rm(n)},300)};G=n,am();try{return await t()}finally{clearInterval(n.dots),im(),G=null}}function om(e,t){let n=G?.ora.isSpinning===!0;n&&G.ora.clear(),e.write(t),n&&G.ora.render()}const sm=e=>W.default.isColorSupported?`\x1b[48;2;95;95;95m\x1b[38;2;250;250;250m${e}\x1b[39m\x1b[49m`:e,q={intro(e){om(process.stdout,`${sm(` skilless `)}${W.default.gray(` v${e}`)}\n\n`)},info(e){om(process.stdout,`${W.default.blue(`·`)} ${e}\n`)},step(e){om(process.stdout,`${W.default.green(`✓`)} ${e}\n`)},warn(e){om(process.stdout,`${W.default.yellow(`!`)} ${e}\n`)},error(e){om(process.stderr,`${W.default.red(`✖`)} ${e}\n`)},dim(e){om(process.stdout,`${W.default.gray(e)}\n`)},blank(){om(process.stdout,`
124
+ `)}},cm=Tc({path:j(),contents:j()}),lm=Tc({url:j(),ref:j().optional(),path:j(),hash:j()}),um=Tc({name:j(),contentHash:j(),editedAt:mc(),updatedAt:mc(),global:N(),source:lm.nullable().default(null)}),dm=um.extend({files:Cc(cm)}),fm=Tc({bound:Cc(j()),unknown:Cc(j())}),pm=Tc({error:j(),message:j().optional()}),mm=new Set([502,503,504]);function hm(e){try{return JSON.parse(e)}catch{return null}}var gm=class{#e;#t;#n=!1;constructor(e,t){this.#e=e,this.#t=t}get baseUrl(){return this.#t}async#r(e,t,n){if(this.#n)throw new rl(this.#t);let r;for(let i=0;i<2;i++){i>0&&await new Promise(e=>setTimeout(e,500));try{let i=await fetch(`${this.#t}/api/v1${t}`,{method:e,headers:{Authorization:`Bearer ${this.#e}`,...n===void 0?{}:{"Content-Type":`application/json`}},body:n===void 0?void 0:JSON.stringify(n),signal:AbortSignal.timeout(1e4)}),a=await i.text();if(mm.has(i.status)){r=Error(`${e} ${t} returned ${i.status}.`);continue}return{status:i.status,text:a}}catch(e){r=e}}throw this.#n=!0,new rl(this.#t,r)}async#i(e,t,n,r){let i=await this.#r(e,t,r);if(i.status===401)throw new tl;if(i.status<200||i.status>=300){let n=pm.safeParse(hm(i.text));throw new P(n.success?n.data.message??n.data.error:`${e} ${t} failed with ${i.status}.`)}if(i.status===204)return n.parse(void 0);let a=n.safeParse(hm(i.text));if(!a.success)throw new P(`Unexpected response from ${e} ${t}.`,{suggestion:`This is likely a version mismatch. Try updating skilless.`});return a.data}async listSkills(){return await this.#i(`GET`,`/skills`,Cc(um))}async getSkill(e){return await this.#i(`GET`,`/skills/${encodeURIComponent(e)}`,dm.nullable())}async putSkill(e,t,n){return await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}`,um,{files:t,editedAt:n})}async setGlobal(e,t){await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}/global`,yc(),{global:t})}async setSource(e,t){await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}/source`,yc(),{source:t})}async deleteSkill(e){await this.#i(`DELETE`,`/skills/${encodeURIComponent(e)}`,yc())}async getBindings(e){return await this.#i(`GET`,`/projects/skills?key=${encodeURIComponent(e)}`,Cc(dm))}async setBindings(e,t){return await this.#i(`PUT`,`/projects/skills`,fm,{key:e,names:t})}};function _m(){return process.env.SKILLESS_TOKEN||cl(Cl,{}).token}function vm(e){ll(Cl,{token:e},384)}function ym(){I(Cl)}function bm(){return(process.env.SKILLESS_API_URL??`https://skilless.dev`).replace(/\/$/,``)}var xm=class{api;#e=null;constructor(){let e=_m();this.api=e?new gm(e,bm()):null}get online(){return this.api!==null&&this.#e===null}async try(e){if(!(!this.api||this.#e))try{return await e(this.api)}catch(e){if(e instanceof rl||e instanceof tl){this.#e=e;return}throw e}}report(){if(this.#e){if(q.blank(),this.#e instanceof rl){q.warn(`Couldn't reach ${this.#e.url}. Run \`skilless sync\` to sync your skills with the cloud.`);return}q.warn(`Your sign-in was rejected, so this only changed this machine.`),q.dim("Run `skilless auth` to sign in again, then `skilless sync` to sync your skills.")}}};function Sm(e){return e.includes(`/`)||e.includes(`:`)}function Cm(e){let t=e.trim(),n,r=t.indexOf(`#`);if(r>=0&&(n=t.slice(r+1)||void 0,t=t.slice(0,r)),/^ext::/i.test(t))throw new P(`The ext:: git transport is not supported.`);let i=/^(github|gitlab):(.+)$/.exec(t);i&&(t=i[1]===`gitlab`?`https://gitlab.com/${i[2]}`:i[2]??``);let a;return a=/^https?:\/\//i.test(t)?wm(t,n):/^[a-z][a-z0-9+.-]*:\/\//i.test(t)||/^[^@/\s]+@[^:/\s]+:/.test(t)?{url:t,ref:n}:Tm(t,n),a.subpath&&=Em(a.subpath),{...a,label:ml(a.url)??a.url}}function wm(e,t){let n;try{n=new URL(e)}catch{throw new P(`${e} is not a valid URL.`)}let r=n.pathname.split(`/`).filter(Boolean).map(decodeURIComponent),i=`${n.protocol}//${n.host}`;if(n.hostname===`github.com`&&r.length>=2){let[e,n,a,o,...s]=r,c=`${i}/${e}/${n?.replace(/\.git$/,``)}.git`;return(a===`tree`||a===`blob`)&&o?{url:c,ref:o,subpath:s.join(`/`)||void 0}:{url:c,ref:t}}let a=r.indexOf(`-`);return a>0&&r[a+1]===`tree`&&r[a+2]?{url:`${i}/${r.slice(0,a).join(`/`).replace(/\.git$/,``)}.git`,ref:r[a+2],subpath:r.slice(a+3).join(`/`)||void 0}:{url:e,ref:t}}function Tm(e,t){let n=/^([^/@\s]+)\/([^/@\s]+)@([^/\s]+)$/.exec(e);if(n)return{url:`https://github.com/${n[1]}/${n[2]}.git`,ref:t,skill:n[3]};let r=/^([^/\s.][^/\s]*)\/([^/\s]+?)(?:\/(.+?))?\/?$/.exec(e);if(!r)throw new P(`Can't tell where ${e} points.`,{suggestion:"Pass `owner/repo` for GitHub, or a full git URL for anywhere else."});return{url:`https://github.com/${r[1]}/${r[2]?.replace(/\.git$/,``)}.git`,ref:t,subpath:r[3]}}function Em(e){let t=e.replace(/\\/g,`/`).split(`/`).filter(Boolean);if(t.includes(`..`))throw new P(`${e} reaches outside the repository.`);return t.join(`/`)}async function Dm(e,t,r){let i=a.mkdtempSync(b.join(s.tmpdir(),`skilless-`));try{let o=[`clone`,`--depth`,`1`,`--quiet`];e.ref&&o.push(`--branch`,e.ref),o.push(`--`,e.url,i);try{await K(`Cloning ${e.label}${e.ref?`#${e.ref}`:``}`,()=>Om(o,{...process.env,GIT_TERMINAL_PROMPT:`0`,GIT_SSH_COMMAND:process.env.GIT_SSH_COMMAND??`ssh -o BatchMode=yes`}))}catch(t){if(!r.interactive)throw km(e,t);I(i),a.mkdirSync(i);try{n(`git`,o,{stdio:[`inherit`,`ignore`,`pipe`],encoding:`utf8`})}catch(t){throw km(e,t)}}return await t(i)}finally{I(i)}}function Om(e,n){return new Promise((r,i)=>{t(`git`,e,{env:n,encoding:`utf8`},(e,t,n)=>{e?i(Object.assign(e,{stderr:n})):r()})})}function km(e,t){let n=t.stderr?.trim();return new P(`Couldn't clone ${e.label}.`,{suggestion:n||`Check the address, and that you have access to the repository.`,cause:t})}const Am=new Set([`node_modules`,`.git`,`dist`,`build`,`__pycache__`]),jm=[`skills`,`skills/.curated`,`skills/.experimental`,`skills/.system`,`.agents/skills`,`.claude/skills`];function Mm(e,t){let n=t?b.join(e,t):e;if(!F(n))throw new P(`${t} does not exist in the repository.`);let r=new Map,i=e=>{let t=Im(e);r.has(t.name)||r.set(t.name,t)};if(Pm(n))return i(n),[...r.values()];Fm(n,1,i);for(let e of jm)Fm(b.join(n,e),3,i);return r.size===0&&Fm(n,5,i),[...r.values()].sort((e,t)=>e.name.localeCompare(t.name))}function Nm(e,t,n){try{let n=Em(t);if(Pm(b.join(e,n)))return{dir:b.join(e,n),path:n}}catch{}let r=Mm(e).find(e=>e.name===n);return r?{dir:r.dir,path:b.relative(e,r.dir)}:null}function Pm(e){return a.existsSync(b.join(e,zl))}function Fm(e,t,n){let r;try{r=a.readdirSync(e,{withFileTypes:!0})}catch{return}for(let i of r.sort((e,t)=>e.name.localeCompare(t.name))){if(!i.isDirectory()||Am.has(i.name))continue;let r=b.join(e,i.name);Pm(r)?n(r):t>1&&Fm(r,t-1,n)}}function Im(e){let t=Lm(a.readFileSync(b.join(e,zl),`utf8`)),n=t.name?.toLowerCase().replace(/[\s_]+/g,`-`);return{name:n&&Ul(n)?n:b.basename(e).toLowerCase(),dir:e,description:t.description}}function Lm(e){let t=/^---\r?\n([\s\S]*?)\r?\n---/.exec(e)?.[1];if(!t)return{};let n=e=>{let n=RegExp(`^${e}:[ \\t]*(.+)$`,`m`).exec(t)?.[1]?.trim();if(!(!n||n===`|`||n===`>`))return n.replace(/^(['"])(.*)\1$/,`$2`)};return{name:n(`name`),description:n(`description`)}}var Rm=`skilless`,zm=`Author skills once, sync them everywhere, install them per project.`;const J=`0.0.3`,Bm=Rm,Vm=zm,Hm=`SKILLESS_TRACE`,Y=Tc({cwd:j()}),X={cwd:new _e(`--cwd <path>`,`The current working directory.`).default(process.cwd()),yes:new _e(`-y, --yes`,`Skip confirmation prompts.`).default(!1),project:new _e(`--project <key>`,`Override the project key. Defaults to the normalized git remote.`)};function Z(e,t){let n=e.safeParse(t);return n.success?n.data:Um(new P(`Invalid options.`,{suggestion:n.error.issues.map(e=>` ${e.message}`).join(`
125
+ `)}))}function Um(e){process.env[Hm]===`1`?console.trace(e):q.error(W.default.red(e.toString())),process.exit(1)}async function Q(e){try{return await e()}catch(e){return e instanceof P?Um(e):Um(new P(e instanceof Error?e.message:String(e),{suggestion:`Run with ${Hm}=1 for a stack trace.`,cause:e}))}}function Wm(){let e=_m();if(!e)throw new tl;return new gm(e,bm())}function Gm(e,t){if(t)return t;let n=hl(e);if(!n)throw new nl;return n}const Km=`~/.agents/skills`;function qm(e){let t=b.normalize(s.homedir());return e===t||e.startsWith(`${t}/`)?`~${e.slice(t.length)}`:e}async function Jm(e,t={}){for(let n of e.skipped){if(n.reason===`tracked`){q.warn(`\`${n.name}\` is tracked by git here, so it was left alone.`);continue}let e=(n.occupied?.paths??[]).map(qm).join(` and `);if(!(t.yes||$p&&await Qp(`Overwrite ${e}?`,!1))){$p?q.dim(`Kept ${e}, so ${n.name} is not linked there.`):(q.warn(`Skipped ${n.name}: ${e} was not made by skilless.`),q.dim(` Run this in a terminal to overwrite it, or pass --yes.`));continue}pu(n,{copy:bu(t.copy)}),q.step(`Overwrote ${e}.`)}for(let t of e.missing??[])q.warn(`${t} is not on this machine yet, so it will be linked on the next sync.`)}const Ym=Y.extend({project:j().optional(),copy:N().optional(),global:N().optional(),notGlobal:N().optional(),overwrite:N(),yes:N()}),Xm=new y(`add`).description(`Add skills to this project, from your library or from a git repository.`).argument(`[skills...]`,"Skills to add, or a repo (`owner/repo` or any git URL) then the skills to take from it. Omit the skills to pick from a list.").option(`-g, --global`,`Make these skills global: linked once into ~/.agents/skills and ~/.claude/skills, so every project has them, including cloud agents.`).option(`--not-global`,`Stop treating these skills as global, and unlink them from your user-level skills.`).option(`--copy`,`Write real files instead of symlinking into the store.`).option(`--overwrite`,`From a repo: replace library skills of the same name.`,!1).addOption(X.yes).addOption(X.project).addOption(X.cwd).action(async(e,t)=>{let n=Z(Ym,t);q.intro(J),await Q(async()=>{let t=new xm,[r,...i]=e;if(r&&Sm(r)){await $m(t,Cm(r),i,n);return}let a=e.find(Sm);if(a)throw new P(`${a} is a repository, not a skill.`,{suggestion:`Name the repository first: \`skilless add ${a} <skills...>\`.`});let{entries:o,live:s}=await K(`Loading your library`,async()=>(await L(t),R(t))),c=e=>s||e.local!==null,l=e=>{let t=o.filter(t=>e.includes(t.name)&&!c(t)).map(e=>e.name);if(t.length!==0)throw new P(`${t.join(`, `)} ${t.length===1?`is`:`are`} not on this machine, and the server can't be reached to fetch ${t.length===1?`it`:`them`}.`,{suggestion:"Run `skilless sync` once you are back online, then try again."})},u=new Set(o.map(e=>e.name)),d=e.filter(e=>!u.has(e));if(d.length>0)throw new P(`Not in your library: ${d.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});if(n.global||n.notGlobal){let r=n.global===!0,i=e;if(i.length===0){let e=o.filter(e=>e.global!==r&&(!r||c(e)));if(e.length===0){q.info(r?`Every skill in your library is already global.`:`No skills in your library are global.`);return}if(i=await tm(r?`Make global`:`Stop being global`,e.map(e=>({name:e.name}))),i.length===0){q.info(`Nothing selected.`);return}}r&&l(i),await Zm(t,i,r,n),t.report();return}let f=Gm(n.cwd,n.project),p=new Set(await K(`Loading ${f}`,()=>Hu(t,f))),m=e;if(m.length===0){let e=o.filter(e=>!p.has(e.name)&&c(e));if(e.length===0){q.info(s?`Every skill in your library is already in this project.`:`Every skill on this machine is already in this project.`),t.report();return}if(m=await tm(`Add to ${f}`,e.map(e=>({name:e.name}))),m.length===0){q.info(`Nothing selected.`);return}}let h=o.filter(e=>e.global).map(e=>e.name);l(m.filter(e=>!h.includes(e))),await Qm(t,f,m,h,n),t.report()})});async function Zm(e,t,n,r){Mu(t,n),await K(`Saving to skilless.dev`,()=>L(e));for(let e of t)q.step(n?`${e} is now global, so every project has it.`:`${e} is no longer global.`);let i=r.project??hl(r.cwd),a=gu(r.cwd),[o,s]=await K(`Updating your skills`,async()=>{if(!i){let t=await qu(e);return[t,t]}let t=await Wu(e,a,i,{copy:r.copy});return[t,t.user]});for(let e of s.written.filter(e=>t.includes(e)))q.dim(`Linked ${e} into ${Km}.`);for(let e of s.removed)q.dim(`Unlinked ${e} from ${Km}.`);if(`project`in o){for(let e of o.project.removed)q.dim(`Moved ${e} out of this project.`);for(let e of o.project.written.filter(e=>t.includes(e)))q.dim(`${e} is still added to this project, so it is linked here again.`)}await Jm(o,r)}async function Qm(e,t,n,r,i){for(let e of n)r.includes(e)&&q.dim(`${e} is global, so it is already in this project.`);let a=n.filter(e=>!r.includes(e));Au(t,a);let o=gu(i.cwd),s=await K(`Updating this project`,async()=>(await L(e),Wu(e,o,t,{copy:i.copy})));for(let e of a)q.step(`Added ${e} to this project.`);await Jm(s,i)}async function $m(e,t,n,r){if(r.notGlobal)throw new P(`--not-global only applies to skills already in your library.`);let i=r.global?null:r.project??hl(r.cwd),a=await K(`Loading your library`,async()=>(await L(e),R(e))),o=new Map(a.entries.map(e=>[e.name,e])),s=[...n,...t.skill?[t.skill]:[]],c=await Dm(t,async e=>{let n=Mm(e,t.subpath);if(n.length===0)throw new P(`No skills found in ${t.label}.`,{suggestion:`A skill is a directory containing a SKILL.md.`});let i=n;if(s.length>0){let e=e=>n.find(t=>t.name===e.toLowerCase()||t.dir.split(`/`).at(-1)?.toLowerCase()===e.toLowerCase()),r=s.filter(t=>!e(t));if(r.length>0)throw new P(`Not in ${t.label}: ${r.join(`, `)}`,{suggestion:`It has: ${n.map(e=>e.name).join(`, `)}`});i=[...new Set(s.map(t=>e(t)))]}else if(n.length>1&&!r.yes){if(!$p)throw new P(`${t.label} has ${n.length} skills.`,{suggestion:`Name the ones you want after the repository, or pass --yes for all of them: ${n.map(e=>e.name).join(`, `)}`});let e=await tm(`Add from ${t.label}`,n.map(e=>({name:e.name,hint:eh(e.description)})));i=n.filter(t=>e.includes(t.name))}let a=[];for(let t of i)try{Wl(t.name),a.push({skill:Kl(t.dir,t.name),path:b.relative(e,t.dir)})}catch(e){q.warn(`Skipped ${t.name}: ${e instanceof P?e.message:e}`)}return a},{interactive:$p});if(c.length===0){q.info(`Nothing selected.`),e.report();return}let l=[],u=[],d=new Map;for(let{skill:e,path:n}of c){d.set(e.name,{url:t.url,...t.ref?{ref:t.ref}:{},path:n,hash:e.contentHash});let i=o.get(e.name),a=i?.local?.contentHash??i?.remote?.contentHash;if(!i||a===e.contentHash){i?zu(e.name,d.get(e.name)):l.push(e),u.push(e.name);continue}if(!(r.overwrite||$p&&!r.yes&&await Qp(`${e.name} is already in your library. Replace it with the one from ${t.label}?`))){q.warn(`Kept your own ${e.name}. Pass --overwrite to replace it.`);continue}l.push(e),u.push(e.name)}let f=await K(`Saving ${l.length} skill(s) to your library`,()=>Ju(e,l,o,d));for(let e of f)q.step(`Copied ${e} into your library from ${t.label}.`);if(u.length===0){e.report();return}r.global?await Zm(e,u,!0,r):i?await Qm(e,i,u,(await K(`Loading your library`,()=>R(e))).entries.filter(e=>e.global).map(e=>e.name),r):(q.blank(),q.dim(`This directory has no git remote, so ${u.length===1?`it is`:`they are`} only in your library. Run \`skilless add ${u[0]}\` inside a project to add ${u.length===1?`it`:`them`} there.`)),e.report()}function eh(e){if(e)return e.length>60?`${e.slice(0,57)}...`:e}function th(e){switch(process.platform){case`darwin`:return[`open`,[e]];case`win32`:return[`rundll32`,[`url.dll,FileProtocolHandler`,e]];default:return[`xdg-open`,[e]]}}function nh(e){let[t,n]=th(e);try{let e=r(t,n,{stdio:`ignore`,detached:!0,windowsHide:!0});e.on(`error`,()=>{}),e.unref()}catch{}}async function rh(){let e=c.randomBytes(16).toString(`hex`),t=bm(),n=new URL(t).origin,r={"Access-Control-Allow-Origin":n,"Access-Control-Allow-Methods":`POST, OPTIONS`,"Access-Control-Allow-Headers":`Content-Type`,"Access-Control-Allow-Private-Network":`true`},i,a=new Promise((e,t)=>{i={resolve:e,reject:t}}),o=d.createServer((t,n)=>{if(t.method===`OPTIONS`){n.writeHead(204,r),n.end();return}if(t.method!==`POST`){n.writeHead(405,r),n.end();return}let a=``;t.on(`data`,e=>{a+=e}),t.on(`end`,()=>{let t;try{t=JSON.parse(a)}catch{n.writeHead(400,r),n.end();return}if(t.state!==e||!t.token){n.writeHead(400,r),n.end();return}n.writeHead(200,{...r,"Content-Type":`application/json`}),n.end(JSON.stringify({ok:!0})),i.resolve(t.token)})}),s=setTimeout(()=>{i.reject(new P(`Timed out waiting for sign in.`,{suggestion:"Run `skilless auth` again, or paste a token with `skilless auth --token <token>`."}))},3e5);try{await new Promise((e,t)=>{o.once(`error`,t),o.listen(0,`127.0.0.1`,e)});let{port:n}=o.address(),r=`${t}/cli/auth?port=${n}&state=${e}`;q.info(`Opening your browser to sign in…`),q.dim(r),q.blank(),nh(r),vm(await K(`Waiting for you to sign in`,()=>a))}finally{clearTimeout(s),o.close()}}const ih=Y.extend({logout:N(),token:j().optional()}),ah=new y(`auth`).description(`Sign in to skilless.dev, or sign out.`).addOption(new _e(`--logout`,`Sign out on this machine.`).default(!1)).addOption(new _e(`--token <token>`,`Save a token directly, for when the browser flow cannot reach localhost.`)).addOption(X.cwd).action(async e=>{let t=Z(ih,e);q.intro(J),await Q(async()=>{if(t.logout){if(!_m()){q.info(`You were not signed in.`);return}ym(),q.step(`Signed out on this machine.`);return}if(t.token){vm(t.token),q.step(`Token saved. You are signed in to skilless.dev.`);return}await rh(),q.step(`Signed in to skilless.dev.`)})}),oh=s.homedir(),sh=process.env.LOCALAPPDATA??b.join(oh,`AppData`,`Local`),ch=process.env.ProgramFiles??`C:/Program Files`,$={cursor:{label:`Cursor`,cli:`cursor`,fallbacks:{darwin:[`/Applications/Cursor.app/Contents/Resources/app/bin/cursor`,`${oh}/Applications/Cursor.app/Contents/Resources/app/bin/cursor`],win32:[b.join(sh,`Programs/cursor/resources/app/bin/cursor.cmd`)],linux:[`/usr/share/cursor/bin/cursor`,`/opt/Cursor/resources/app/bin/cursor`]}},vscode:{label:`VS Code`,cli:`code`,fallbacks:{darwin:[`/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`,`${oh}/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`],win32:[b.join(sh,`Programs/Microsoft VS Code/bin/code.cmd`),b.join(ch,`Microsoft VS Code/bin/code.cmd`)],linux:[`/usr/share/code/bin/code`,`/snap/bin/code`]}},windsurf:{label:`Windsurf`,cli:`windsurf`,fallbacks:{darwin:[`/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf`,`${oh}/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf`],win32:[b.join(sh,`Programs/Windsurf/bin/windsurf.cmd`)],linux:[`/usr/share/windsurf/bin/windsurf`]}},zed:{label:`Zed`,cli:`zed`,fallbacks:{darwin:[`/Applications/Zed.app/Contents/MacOS/cli`,`${oh}/Applications/Zed.app/Contents/MacOS/cli`],win32:[b.join(sh,`Programs/Zed/bin/zed.exe`)],linux:[`${oh}/.local/bin/zed`]}},sublime:{label:`Sublime Text`,cli:`subl`,fallbacks:{darwin:[`/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl`],win32:[b.join(ch,`Sublime Text/subl.exe`)],linux:[`/opt/sublime_text/sublime_text`]}},neovim:{label:`Neovim`,cli:`nvim`,terminal:!0},vim:{label:`Vim`,cli:`vim`,terminal:!0}},lh=Object.keys($);function uh(e){return Object.hasOwn($,e)}function dh(e){let t=i(process.platform===`win32`?`where`:`which`,[e],{encoding:`utf8`,windowsHide:!0});if(t.status!==0||!t.stdout)return null;let n=t.stdout.split(/\r?\n/).map(e=>e.trim()).filter(Boolean);return process.platform===`win32`?n.find(e=>/\.(exe|cmd|bat)$/i.test(e))??n[0]??null:n[0]??null}function fh(e){let t=$[e],n=dh(t.cli);if(n)return n;let r=process.platform;return t.fallbacks?.[r]?.find(e=>F(e))??null}function ph(){return lh.filter(e=>fh(e)!==null)}function mh(e){return $[e].terminal===!0}function hh(e,t){let n=fh(e);if(!n)return Promise.reject(Error(`${$[e].label} was not found. Is \`${$[e].cli}\` on your PATH?`));let i=mh(e);return new Promise((e,a)=>{let o=r(n,[t],{cwd:b.dirname(t),stdio:i?`inherit`:`ignore`,detached:!i,windowsHide:!0,shell:process.platform===`win32`&&/\.(cmd|bat)$/i.test(n)});o.on(`error`,a),i?o.on(`exit`,()=>e()):(o.unref(),setImmediate(e))})}function gh(){let e=cl(Ol,{});return e.editor!==void 0&&!uh(e.editor)&&delete e.editor,e}function _h(e){ll(Ol,e)}function vh(e){let t={...gh(),...e};for(let e of Object.keys(t))t[e]===void 0&&delete t[e];return _h(t),t}const yh=`none`,bh=Tc({unset:N()}),xh=new y(`editor`).description("Choose the editor `skilless create` opens a new skill in.").argument(`[editor]`,`Set it directly: ${lh.join(`, `)}.`).addOption(new _e(`--unset`,`Stop opening new skills in an editor.`).default(!1)).action(async(e,t)=>{let n=Z(bh,t);q.intro(J),await Q(async()=>{let t=gh().editor;if(n.unset){vh({editor:void 0}),q.step(`New skills will no longer open in an editor.`);return}if(e){if(!uh(e))throw new P(`${e} is not an editor skilless knows.`,{suggestion:`Pick one of: ${lh.join(`, `)}.`});vh({editor:e}),q.step(`New skills will open in ${$[e].label}.`);return}if(!$p){q.info(t?`Editor: ${$[t].label}`:`No editor set.`);return}let r=ph();if(r.length===0)throw new P(`None of the editors skilless knows are installed.`,{suggestion:`skilless looks for: ${lh.map(e=>$[e].cli).join(`, `)}.`});let i=await nm(`Open new skills in`,[...r.map(e=>({name:e,message:$[e].label,hint:e===t?W.default.gray(`current`):void 0})),{name:yh,message:`None`,hint:t?void 0:W.default.gray(`current`)}],t??yh);if(i===yh){vh({editor:void 0}),q.step(`New skills will not open in an editor.`);return}uh(i)&&(vh({editor:i}),q.step(`New skills will open in ${$[i].label}.`))})}),Sh=new y(`config`).description(`View or change your skilless settings.`).addCommand(xh).action(async()=>{q.intro(J),await Q(async()=>{let e=gh();q.info(`editor ${e.editor?$[e.editor].label:W.default.gray(`not set`)}`),q.blank(),q.dim(`Stored in ${Ol}`)})}),Ch=Y.extend({description:j().optional(),project:j().optional(),global:N().optional(),open:N()}),wh=new y(`create`).description(`Create a skill, add it to this project, and print the path to edit it.`).argument(`[name]`,`The name of the skill. You will be asked if you leave it out.`).option(`-d, --description <description>`,`What the skill does, for the frontmatter.`).option(`-g, --global`,`Make it global: linked once into ~/.agents/skills and ~/.claude/skills, so every project has it, including cloud agents.`).option(`--no-open`,"Do not open SKILL.md in your editor (see `skilless config editor`).").addOption(X.project).addOption(X.cwd).action(async(e,t)=>{let n=Z(Ch,t);q.intro(J),await Q(async()=>{let t=new xm,r=await K(`Loading your library`,async()=>(await L(t),R(t))),i=new Set(r.entries.map(e=>e.name)),a=e;if(!a){if(!$p)throw new P(`No name given.`,{suggestion:"Run `skilless create <name>`."});a=await em(`Name`,{validate:e=>e?Ul(e)?i.has(e)?`${e} already exists in your library.`:!0:`Use lowercase letters, digits, dots, dashes and underscores.`:`A name is required.`})}if(Wl(a),Xl(a))throw new P(`${a} already exists in your library.`,{suggestion:`Edit it at ${b.join(Al(a),`SKILL.md`)}`});if(i.has(a))throw new P(`${a} already exists in your library.`,{suggestion:`Run \`skilless sync\` to bring ${a} down to this machine.`});let o=n.description??await em(`What does this skill do?`);ql(a,Zl(a,o));let s=Kl(Al(a),a);if(await K(`Creating ${a}`,()=>t.try(e=>e.putSkill(a,s.files,s.editedAt)))){let e=vu();e.skills[a]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:Date.now()},yu(e)}q.step(`Created ${a} in your library.`),n.global&&Mu([a],!0);let c=n.project??hl(n.cwd);if(c){n.global||Au(c,[a]);let e=gu(n.cwd);await Jm(await K(`Updating this project`,async()=>(await L(t),Wu(t,e,c))))}else n.global?await Jm(await K(`Linking your global skills`,async()=>(await L(t),qu(t)))):await K(`Saving to skilless.dev`,()=>L(t));n.global?q.step(`${a} is now global, so every project has it.`):c?q.step(`Added ${a} to ${c}.`):q.warn(`No git remote here, so ${a} was not added to a project.`);let l=b.join(Al(a),`SKILL.md`),u=n.open?gh().editor:void 0,d=u&&(!mh(u)||$p);if(q.blank(),u&&d)try{q.dim(`Opening ${a} in ${$[u].label}...`),await hh(u,l)}catch(e){q.warn(e instanceof Error?e.message:String(e)),q.dim(`Edit ${a} at ${l}`)}else q.dim(`Edit ${a} at ${l}`);t.api&&q.dim("Run `skilless sync` to save your changes."),t.report()})}),Th=Y.extend({yes:N()}),Eh=new y(`delete`).description(`Delete skills from your library, everywhere.`).argument(`<skills...>`,`Skills to delete.`).addOption(X.yes).addOption(X.cwd).action(async(e,t)=>{let n=Z(Th,t);q.intro(J),await Q(async()=>{let t=new xm,r=await K(`Loading your library`,async()=>(await L(t),R(t))),i=new Set(r.entries.map(e=>e.name)),a=e.filter(e=>!i.has(e));if(a.length>0)throw new P(`Not in your library: ${a.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});if(q.warn(`This deletes ${e.join(`, `)} from every project, on every machine.`),q.dim(`Run \`skilless remove ${e.join(` `)}\` to take ${e.length===1?`it`:`them`} out of just this project.`),!(n.yes||await Qp(`Delete ${e.join(`, `)}?`,!1))){q.info(`Nothing was deleted.`);return}Pu(e),Bu(e);let o=vu(),s=gu(n.cwd);for(let t of e)I(Al(t)),delete o.skills[t],q.step(`Deleted ${t} from your library.`);yu(o),mu(s,e),mu($l(),e),await K(`Deleting from skilless.dev`,()=>L(t)),q.blank(),q.dim("Other checkouts will clean up on their next `skilless install`."),q.dim(`Restore at skilless.dev/skills within 30 days.`),t.report()})}),Dh=Y.extend({yes:N(),overwrite:N()});function Oh(e){return a.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(t=>({name:t.name,dir:b.join(e,t.name)})).filter(e=>F(b.join(e.dir,zl))).sort((e,t)=>e.name.localeCompare(t.name))}const kh=new y(`import`).description(`Import a directory of existing skills into your library.`).argument(`<dir>`,`A directory whose subdirectories each contain a SKILL.md.`).option(`--overwrite`,`Replace skills that already exist.`,!1).addOption(X.yes).addOption(X.cwd).action(async(e,t)=>{let n=Z(Dh,t);q.intro(J),await Q(async()=>{let t=new xm,r=b.resolve(n.cwd,e);if(!F(r))throw new P(`${r} does not exist.`);let i=Oh(r);if(i.length===0)throw new P(`No skills found in ${r}.`,{suggestion:`A skill is a directory containing a ${zl}.`});let a=await K(`Loading your library`,async()=>(await L(t),R(t))),o=new Map(a.entries.map(e=>[e.name,e])),s=[],c=[];for(let e of i){if(!Ul(e.name)){c.push({name:e.name,reason:`invalid name`});continue}if(o.has(e.name)&&!n.overwrite){c.push({name:e.name,reason:`already in your library`});continue}try{s.push(Kl(e.dir,e.name))}catch(t){c.push({name:e.name,reason:t instanceof Error?t.message:`unreadable`})}}for(let e of c)q.warn(`Skipped ${e.name}: ${e.reason}.`);if(s.length===0){q.info(`Nothing to import.`),t.report();return}if(q.blank(),q.info(`Importing: ${s.map(e=>e.name).join(`, `)}`),!(n.yes||await Qp(`Import ${s.length} skill(s)?`,!0)))return;let l=await K(`Importing ${s.length} skill(s)`,()=>Ju(t,s,o));for(let e of l)q.step(`Imported ${e} into your library.`);q.blank(),q.dim(`Run \`skilless add ${s[0]?.name??`<skill>`}\` to add one to this project.`),t.report()})}),Ah=new y(`init`).description(`Create the local skilless directory.`).addOption(X.cwd).action(async e=>{Z(Y,e),q.intro(J),await Q(async()=>{let e=!F(Sl);al(Sl),q.step(`${e?`Created`:`Found`} your library at ${xl}.`),q.blank(),q.dim("Run `skilless create <name>` to make a skill."),q.dim("Run `skilless import <dir>` to bring in skills you already have."),_m()||q.dim("Run `skilless auth` to sync your skills with the cloud.")})}),jh=Y.extend({project:j().optional(),copy:N().optional()}),Mh=new y(`install`).description(`Install every skill added to this project, and your global skills at the user level.`).option(`--copy`,`Write real files instead of symlinking into the store.`).addOption(X.project).addOption(X.cwd).action(async e=>{let t=Z(jh,e);q.intro(J),await Q(async()=>{let e=Wm(),n=Gm(t.cwd,t.project),r=gu(t.cwd),i=await K(`Installing ${n}'s skills`,async()=>(await Fu(e),Cu(e,r,n,{copy:t.copy}))),{project:a,user:o}=i;for(let e of[...a.pruned,...o.pruned])q.dim(`Pruned ${e}, which is no longer in your library.`);for(let e of[...a.fetched,...o.fetched])q.dim(`Fetched ${e} from your library.`);for(let e of a.removed)q.dim(`Removed ${e}, which is no longer in this project.`);for(let e of o.removed)q.dim(`Removed ${e} from ${Km}, since it is no longer global.`);for(let e of a.written)q.step(`Installed ${e}.`);for(let e of o.written)q.step(`Installed ${e} globally.`);if(i.count===0){q.info(`No skills added to ${n} yet.`),q.dim("Run `skilless add <skill>` to add one.");return}await Jm(i,{copy:t.copy}),q.blank(),q.dim(bu(t.copy)?`Wrote real files into .agents/skills, and global skills into ~/.agents/skills.`:`Linked .agents/skills, and global skills in ~/.agents/skills, to your library.`)})}),Nh=Y.extend({project:Dc([N(),j()]).optional()});function Ph(e,t){let n=[];return e.global&&n.push(`global`),e.source&&n.push(`from ${ml(e.source.url)??e.source.url}`),e.local?t&&e.unsynced&&n.push(`not synced`):n.push(`not on this machine`),n.map(e=>` · ${e}`).join(``)}const Fh=new y(`list`).description(`List your skills, or the ones added to this project.`).option(`-p, --project [key]`,`List this project’s skills instead of your library.`).addOption(X.cwd).action(async e=>{let t=Z(Nh,e);q.intro(J),await Q(async()=>{let e=new xm,n=await K(`Loading your library`,async()=>(await L(e),R(e)));if(t.project!==void 0){let r=Gm(t.cwd,typeof t.project==`string`?t.project:void 0),i=Uu(n,await K(`Loading ${r}`,()=>Hu(e,r)));q.info(`${W.default.bold(r)}`),q.blank(),i.length===0&&q.dim("No skills in this project. Run `skilless add <skill>` to add one.");for(let t of i)q.dim(` ${t.name}${W.default.gray(Ph(t,e.api!==null))}`);e.report();return}n.entries.length===0&&q.dim("Your library is empty. Run `skilless create <name>` to make one.");for(let t of n.entries){let n=t.local?.editedAt??t.remote?.updatedAt??0,r=new Date(n).toISOString().slice(0,10);q.dim(` ${t.name.padEnd(28)} ${W.default.gray(r+Ph(t,e.api!==null))}`)}e.report()})}),Ih=Y.extend({yes:N(),user:N().optional()});function Lh(e,t){return F(e)?a.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.isSymbolicLink()).map(n=>({name:n.name,dir:b.join(e,n.name),scope:t})).filter(e=>F(b.join(e.dir,zl))):[]}const Rh=new y(`migrate`).description(`Move this project's skills into your library and link them back. Offers your user-level skills too, as globals linked back at the user level.`).option(`--user`,`Migrate your user-level skills too, without asking.`).addOption(X.yes).addOption(X.cwd).action(async e=>{let t=Z(Ih,e);q.intro(J),await Q(async()=>{let e=new xm,n=gu(t.cwd),r=hl(t.cwd),i=r?[jl,Ml].flatMap(e=>Lh(b.join(n,e),`project`).filter(e=>{let t=vl(n,b.relative(n,e.dir));return t&&q.warn(`${e.name} is committed to this repo, so it was left alone. Remove it from git first to migrate it.`),!t})):[],a=[...Lh(Fl(),`user`),...Lh(Pl(),`user`)];if(r||q.dim(`No git remote here, so there are no project skills to migrate.`),i.length===0&&a.length===0){q.info(`Nothing to migrate.`);return}let o=await K(`Loading your library`,async()=>(await L(e),R(e))),s=new Map(o.entries.map(e=>[e.name,e])),c=async e=>{let t=new Map;for(let n of e)t.set(n.name,[...t.get(n.name)??[],n]);let n=[];for(let[e,r]of[...t].sort(([e],[t])=>e.localeCompare(t))){if(!Ul(e)){q.warn(`Skipped ${e}: not a valid skill name.`);continue}let t;try{t=r.map(t=>Kl(t.dir,e))}catch(t){q.warn(`Skipped ${e}: ${t instanceof Error?t.message:`unreadable`}.`);continue}let i=t[0];if(!i)continue;if(t.some(e=>e.contentHash!==i.contentHash)){q.warn(`Skipped ${e}: found more than one version (${r.map(e=>e.dir).join(`, `)}).`);continue}let a=s.get(e),o=a?.local?.contentHash??a?.remote?.contentHash;if(o&&o!==i.contentHash){q.warn(`Skipped ${e}: a different ${e} is already in your library. Rename one to migrate it.`);continue}n.push({skill:i,dirs:r.map(e=>e.dir),scope:r.some(e=>e.scope===`user`)?`user`:`project`,adopted:o!==void 0})}return n},l=await c(i);if(l.length>0&&(q.info(`Project skills in ${r}: ${l.map(e=>e.skill.name).join(`, `)}`),t.yes||await Qp(`Move ${l.length} skill(s) into skilless and link them here?`,!0)||(l=[])),a.length>0){let e=[...new Set(a.map(e=>e.name))].sort();if(q.info(`User-level skills: ${e.join(`, `)}`),t.user||!t.yes&&$p&&await Qp(`Would you also like to migrate your user-level skills? They become global and are linked back into ~/.agents/skills, so every project still gets them.`,!1)){let e=new Set(l.map(e=>e.skill.name)),t=await c([...a,...i.filter(e=>a.some(t=>t.name===e.name))]);l=[...l.filter(e=>!t.some(t=>t.skill.name===e.skill.name)),...t];for(let t of e)l.some(e=>e.skill.name===t)||q.warn(`Skipped ${t}: its project and user-level copies differ.`)}}if(l.length===0){q.info(`Nothing was migrated.`);return}let u=vu();for(let{skill:t,adopted:n}of l)ql(t.name,t.files,t.editedAt),!n&&(await K(`Saving ${t.name} to your library`,()=>e.try(e=>e.putSkill(t.name,t.files,t.editedAt)))?u.skills[t.name]={contentHash:t.contentHash,editedAt:t.editedAt,syncedAt:Date.now()}:delete u.skills[t.name]);yu(u);for(let{dirs:e}of l)for(let t of e)I(t);let d=l.filter(e=>e.scope===`user`).map(e=>e.skill.name),f=l.filter(e=>e.scope===`project`).map(e=>e.skill.name);d.length>0&&Mu(d,!0),r&&f.length>0&&Au(r,f),await K(`Saving to skilless.dev`,()=>L(e));for(let e of f)q.step(`Migrated ${e} into your library and this project.`);for(let e of d)q.step(`Migrated ${e} into your library as a global skill.`);r?await Jm(await K(`Updating this project`,()=>Wu(e,n,r)),{yes:t.yes}):d.length>0&&await Jm(await K(`Linking your global skills`,()=>qu(e)),{yes:t.yes}),e.report()})}),zh=Y.extend({project:j().optional(),copy:N().optional()}),Bh=new y(`remove`).description(`Remove skills from this project. They stay in your library.`).argument(`<skills...>`,`Skills to remove.`).option(`--copy`,`Write real files instead of symlinking into the store.`).addOption(X.project).addOption(X.cwd).action(async(e,t)=>{let n=Z(zh,t);q.intro(J),await Q(async()=>{let t=new xm,r=Gm(n.cwd,n.project),[i,a]=await K(`Loading your library`,async()=>(await L(t),[await R(t),new Set(await Hu(t,r))])),o=new Set(i.entries.filter(e=>e.global).map(e=>e.name)),s=[];for(let t of e){if(o.has(t)){q.warn(`${t} is global, so it is in every project.`),q.dim(` Run \`skilless add --not-global ${t}\` to take it out of every project.`);continue}if(!a.has(t)){q.warn(`${t} is not in this project.`);continue}s.push(t)}if(s.length===0){t.report();return}ju(r,s);let c=gu(n.cwd),l=await K(`Updating this project`,async()=>(await L(t),Wu(t,c,r,{copy:n.copy})));for(let e of s)q.step(`Removed ${e} from this project.`);await Jm(l,{copy:n.copy}),q.blank(),q.dim(`Run \`skilless delete ${s.join(` `)}\` to delete ${s.length===1?`it`:`them`}.`),t.report()})});function Vh(e,t,n,r){let i=[...new Set([...e.keys(),...t.keys()])].sort(),a=[];for(let o of i){let i=e.get(o),s=t.get(o),c=n.skills[o];if(i&&s){if(i.contentHash===s.contentHash){a.push({type:`noop`,name:o});continue}let e=!c||c.contentHash!==i.contentHash,t=!c||c.contentHash!==s.contentHash;if(e&&!t)a.push({type:`push`,name:o});else if(!e&&t)a.push({type:`pull`,name:o});else{let e=r??(i.editedAt>=s.editedAt?`push`:`pull`);a.push({type:e===`push`?`conflict-push`:`conflict-pull`,name:o})}continue}if(i&&!s){a.push({type:c?`delete-local`:`push`,name:o});continue}!i&&s&&a.push({type:c?`delete-remote`:`pull`,name:o})}return a}function Hh(e,t,n){return Uh(e,t===`push`?`remote`:`local`,n)}function Uh(e,t,n){let r=new Date().toISOString().replace(/[:.]/g,`-`),i=b.join(kl,e,`${r}-${t}`);for(let e of n)sl(b.join(i,e.path),e.contents);return i}async function Wh(e,t,n){let r={actions:[],conflicts:[],skipped:[]},i=Date.now();for(let a of t){let{name:t}=a,o=n.local.get(t);switch(a.type){case`noop`:o&&(n.state.skills[t]={contentHash:o.contentHash,editedAt:o.editedAt,syncedAt:i});break;case`push`:case`conflict-push`:if(!o)break;if(a.type===`conflict-push`){let n=await e.getSkill(t);n&&r.conflicts.push({name:t,kept:`push`,stashedAt:Hh(t,`push`,n.files)})}await e.putSkill(t,o.files,o.editedAt),n.state.skills[t]={contentHash:o.contentHash,editedAt:o.editedAt,syncedAt:i};break;case`pull`:case`conflict-pull`:{let s=await e.getSkill(t);if(!s)break;a.type===`conflict-pull`&&o&&r.conflicts.push({name:t,kept:`pull`,stashedAt:Hh(t,`pull`,o.files)}),ql(t,s.files,s.editedAt),n.state.skills[t]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:i};break}case`delete-local`:if(!(n.yes||await Qp(`Delete ${t} from this machine? It was deleted elsewhere.`,!0))){r.skipped.push(t);continue}I(Al(t)),delete n.state.skills[t];break;case`delete-remote`:if(!(n.yes||await Qp(`Delete ${t} everywhere? It was deleted on this machine.`,!0))){r.skipped.push(t);continue}await e.deleteSkill(t),delete n.state.skills[t];break}r.actions.push(a)}return r}function Gh(e){switch(e.type){case`push`:return`Pushed ${e.name} to skilless.dev.`;case`pull`:return`Pulled ${e.name} from skilless.dev.`;case`conflict-push`:return`${e.name} changed in both places. Kept the copy on this machine.`;case`conflict-pull`:return`${e.name} changed in both places. Kept the copy from skilless.dev.`;case`delete-local`:return`Deleted ${e.name} from this machine.`;case`delete-remote`:return`Deleted ${e.name} everywhere.`;default:return null}}const Kh=Y.extend({yes:N(),push:N().optional(),pull:N().optional()}),qh=new y(`sync`).description(`Sync your library with skilless.dev, in both directions, and update this project's skills and your global ones.`).option(`--push`,`On a conflict, keep the local copy.`).option(`--pull`,`On a conflict, keep the remote copy.`).addOption(X.yes).addOption(X.cwd).action(async e=>{let t=Z(Kh,e);q.intro(J),await Q(async()=>{let e=Wm(),[n,r]=await K(`Checking skilless.dev`,async()=>[await Fu(e),await e.listSkills()]),i=new Map(Yl().map(e=>[e.name,e])),a=new Map(r.map(e=>[e.name,e])),o=vu(),s=Vh(i,a,o,t.push?`push`:t.pull?`pull`:void 0),c=await K(`Syncing your library`,()=>Wh(e,s,{local:i,state:o,yes:t.yes}));yu(o);let l=c.actions.map(Gh).filter(e=>e!==null);for(let e of l)q.step(e);let[u,d]=await K(`Syncing your library`,async()=>{let t=n+await Fu(e);return[t,l.length>0||t>0?await e.listSkills():r]});u>0&&q.step(`Sent ${u} change(s) made while offline.`),Ll(d),Vu(d);let f=hl(t.cwd),p=await K(f?`Updating this project`:`Updating your global skills`,async()=>{if(!f)return{user:Tu(d.filter(e=>e.global).map(e=>e.name))};let n=await Cu(e,gu(t.cwd),f);return{...n,project:n.project}}),m=`project`in p?p.project:void 0,{user:h}=p;if(m){for(let e of m.added)q.step(`Linked ${e} into this project.`);for(let e of m.removed)q.step(`Unlinked ${e}, which is no longer in this project.`);for(let e of m.pruned)q.step(`Unlinked ${e}, which is no longer in your library.`)}for(let e of h.added)q.step(`Linked ${e} into ${Km}.`);for(let e of h.removed)q.step(`Unlinked ${e} from ${Km}, since it is no longer global.`);for(let e of h.pruned)q.step(`Unlinked ${e} from ${Km}, since it is no longer in your library.`);let g=[m,h].reduce((e,t)=>e+(t?t.added.length+t.removed.length+t.pruned.length:0),0);if(l.length===0&&g===0&&u===0&&q.info(`Everything is already in sync.`),c.conflicts.length>0){q.blank(),q.warn(`${c.conflicts.length} skill(s) changed in two places. The copy that lost is kept here:`);for(let e of c.conflicts)q.dim(` ${e.stashedAt}`)}await Jm({skipped:[...m?.skipped??[],...h.skipped]},{yes:t.yes}),c.skipped.length>0&&(q.blank(),q.dim(`Skipped ${c.skipped.join(`, `)}.`))})}),Jh=Y.extend({yes:N(),force:N()});function Yh(e){let t=ml(e.url)??e.url;return e.ref?`${t}#${e.ref}`:t}const Xh=new y(`update`).description(`Update skills added from a git repository to what the repository has now.`).argument(`[skills...]`,`Skills to update. Omit to update every skill added from a repository.`).option(`-f, --force`,`Replace skills you have edited too. Your copy is kept first.`,!1).addOption(X.yes).addOption(X.cwd).action(async(e,t)=>{let n=Z(Jh,t);q.intro(J),await Q(async()=>{let t=new xm,r=await K(`Loading your library`,async()=>(await L(t),R(t))),i=new Map(r.entries.map(e=>[e.name,e])),a=r.entries.filter(e=>e.source!==null);if(e.length>0){let t=e.filter(e=>!i.has(e));if(t.length>0)throw new P(`Not in your library: ${t.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});let n=e.filter(e=>!i.get(e)?.source);if(n.length>0)throw new P(`${n.join(`, `)} ${n.length===1?`was`:`were`} not added from a repository, so there is nothing to update from.`)}let o=e.length>0?a.filter(t=>e.includes(t.name)):a;if(o.length===0){q.info(`None of your skills were added from a repository.`),q.dim("Run `skilless add owner/repo` to add some."),t.report();return}let s=new Map;for(let e of o){let t=`${e.source.url}#${e.source.ref??``}`;s.set(t,[...s.get(t)??[],e])}let c=[],l=new Map,u=[],d=[],f=[],p=[];for(let e of s.values()){let r=e[0].source,i=Yh(r);await Dm({url:r.url,ref:r.ref,label:i},async r=>{for(let a of e){let e=Nm(r,a.source.path,a.name);if(!e){q.warn(`${a.name} is no longer in ${i}.`);continue}let o;try{o=Kl(e.dir,a.name)}catch(e){q.warn(`Skipped ${a.name}: ${e instanceof Error?e.message:e}`);continue}let s={...a.source,path:e.path,hash:o.contentHash},p=a.local?.contentHash??a.remote?.contentHash;if(o.contentHash===a.source.hash||p===o.contentHash){zu(a.name,s),u.push(a.name);continue}if(p!==a.source.hash){if(!(n.force||$p&&!n.yes&&await Qp(`You have edited ${a.name} since it came from ${i}. Replace your edits with what it has now?`))){d.push(a.name);continue}let e=a.local?.files??(await K(`Fetching your copy of ${a.name}`,()=>t.try(e=>e.getSkill(a.name))))?.files;if(!e){q.warn(`Couldn't fetch your copy of ${a.name} to keep it, so it was left alone.`);continue}f.push({name:a.name,dir:Uh(a.name,`update`,e)})}c.push(o),l.set(a.name,s)}},{interactive:$p}).then(()=>!0,e=>{if(!(e instanceof P))throw e;return q.warn(e.toString()),!1})||p.push(...e.map(e=>e.name))}q.blank();let m=await K(`Saving ${c.length} skill(s) to your library`,()=>Ju(t,c,i,l));for(let e of m)q.step(`Updated ${e}.`);if(u.length>0&&q.info(u.length===1?`${u[0]} is already up to date.`:`${u.length} skills are already up to date.`),d.length>0&&q.warn(`Kept your edits to ${d.join(`, `)}. Run \`skilless update ${d.join(` `)} --force\` to replace them.`),p.length>0&&q.warn(`Couldn't check ${p.join(`, `)}.`),f.length>0){q.blank(),q.dim(`Your edited copies are kept here:`);for(let e of f)q.dim(` ${e.dir}`)}t.report()})});le.name(Bm).description(Vm).version(J).addCommand(Ah).addCommand(ah).addCommand(wh).addCommand(Xm).addCommand(Xh).addCommand(Bh).addCommand(Eh).addCommand(Mh).addCommand(qh).addCommand(kh).addCommand(Rh).addCommand(Fh).addCommand(Sh).parse();export{};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "skilless",
3
3
  "description": "Author skills once, sync them everywhere, install them per project.",
4
- "version": "0.0.2",
4
+ "version": "0.0.3",
5
5
  "license": "MIT",
6
6
  "homepage": "https://skilless.dev",
7
7
  "author": {