skilless 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +2 -2
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -122,5 +122,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
122
122
|
`)}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+`
|
|
123
123
|
`)),this}return this.isSpinning?this:(this.#r.hideCursor&&sp.hide(this.#a),this.#r.discardStdin&&o.stdin.isTTY&&(tm.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&&sp.show(this.#a)),this.#d&&(this.#d=!1,tm.stop()),this}succeed(e){return this.stopAndPersist({symbol:_p,text:e})}fail(e){return this.stopAndPersist({symbol:yp,text:e})}warn(e){return this.stopAndPersist({symbol:vp,text:e})}info(e){return this.stopAndPersist({symbol:gp,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)+`
|
|
124
124
|
`;return this.stop(),this.#p(()=>this.#a.write(a)),this}};function am(e){return new im(e)}var G=te(y(((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 om=Zd.default.prompt;function sm(){process.stdout.write(`\r\x1B[0J`),process.stdin.isTTY&&process.stdin.setRawMode(!1),um()}const cm={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 lm(e,t){gm();try{return await om({...e,cancel:sm})}catch{return um(),t}finally{_m()}}function um(){gm(),process.stdout.write(`${G.default.yellow(`!`)} Cancelled\n`),process.exit(1)}async function dm(e,t=!1){return(await lm({type:`confirm`,name:`value`,message:e,initial:t},{value:!1})).value}const K=!!(process.stdin.isTTY&&process.stdout.isTTY);async function fm(e,t={}){return(await lm({type:`input`,name:`value`,message:e,initial:t.initial,validate:t.validate?e=>t.validate((e??``).trim()):void 0},{value:``})).value.trim()}async function pm(e,t){return t.every(e=>e.disabled)?[]:(await lm({type:`multiselect`,name:`value`,message:e,choices:t,...cm},{value:[]})).value}async function mm(e,t,n){let r=n?t.findIndex(e=>e.name===n):-1;return(await lm({type:`select`,name:`value`,message:e,choices:t,initial:r===-1?0:r,...cm},{value:``})).value}let q=null;function hm(e){let t=e.step%3+1;return`${e.message}${`.`.repeat(t)}${` `.repeat(3-t)}`}function gm(){q?.ora.isSpinning&&q.ora.stop()}function _m(){q&&!q.ora.isSpinning&&q.ora.start(hm(q))}async function J(e,t){if(!process.stdout.isTTY||!process.stdout.columns)return t();if(q){let n=q.message;q.message=e;try{return await t()}finally{q&&(q.message=n)}}let n={ora:am({stream:process.stdout,color:`cyan`,discardStdin:!1}),message:e,step:0,dots:setInterval(()=>{n.step++,n.ora.text=hm(n)},300)};q=n,_m();try{return await t()}finally{clearInterval(n.dots),gm(),q=null}}function vm(e,t){let n=q?.ora.isSpinning===!0;n&&q.ora.clear(),e.write(t),n&&q.ora.render()}const ym=e=>G.default.isColorSupported?`\x1b[48;2;95;95;95m\x1b[38;2;250;250;250m${e}\x1b[39m\x1b[49m`:e;function bm(e,t){let n=Math.min(process.stdout.columns||80,80)-2,r=[],i=``;for(let[a,o]of e.entries()){let s=a<e.length-1?`${o}${t.trimEnd()}`:o;i&&i.length+1+s.length>n?(r.push(i),i=s):i=i?`${i} ${s}`:s}return i&&r.push(i),r.map(e=>` ${e}\n`).join(``)}const Y={intro(e){vm(process.stdout,`${ym(` skilless `)}${G.default.gray(` v${e}`)}\n\n`)},info(e){vm(process.stdout,`${G.default.blue(`·`)} ${e}\n`)},step(e){vm(process.stdout,`${G.default.green(`✓`)} ${e}\n`)},warn(e){vm(process.stdout,`${G.default.yellow(`!`)} ${e}\n`)},error(e){vm(process.stderr,`${G.default.red(`✖`)} ${e}\n`)},dim(e){vm(process.stdout,`${G.default.gray(e)}\n`)},list(e,t,n=!1){let r=n?G.default.green(`✓`):G.default.blue(`·`);vm(process.stdout,`${r} ${e}\n${G.default.cyan(bm(t,`, `))}`)},note(e){vm(process.stdout,G.default.gray(bm(e.split(/\s+/),` `)))},blank(){vm(process.stdout,`
|
|
125
|
-
`)}},xm=Cc({path:P(),contents:P()}),Sm=Cc({url:P(),ref:P().optional(),path:P(),hash:P()}),Cm=Cc({name:P(),contentHash:P(),editedAt:fc(),updatedAt:fc(),global:I(),source:Sm.nullable().default(null)}),wm=Cm.extend({files:xc(xm)}),Tm=Cc({bound:xc(P()),unknown:xc(P())}),Em=Cc({error:P(),message:P().optional()}),Dm=new Set([502,503,504]);function Om(e){try{return JSON.parse(e)}catch{return null}}var km=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 tl(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(Dm.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 tl(this.#t,r)}async#i(e,t,n,r){let i=await this.#r(e,t,r);if(i.status===401)throw new $c;if(i.status<200||i.status>=300){let n=Em.safeParse(Om(i.text));throw new L(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(Om(i.text));if(!a.success)throw new L(`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`,xc(Cm))}async getSkill(e){return await this.#i(`GET`,`/skills/${encodeURIComponent(e)}`,wm.nullable())}async putSkill(e,t,n){return await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}`,Cm,{files:t,editedAt:n})}async setGlobal(e,t){await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}/global`,_c(),{global:t})}async setSource(e,t){await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}/source`,_c(),{source:t})}async deleteSkill(e){await this.#i(`DELETE`,`/skills/${encodeURIComponent(e)}`,_c())}async getBindings(e){return await this.#i(`GET`,`/projects/skills?key=${encodeURIComponent(e)}`,xc(wm))}async setBindings(e,t){return await this.#i(`PUT`,`/projects/skills`,Tm,{key:e,names:t})}},Am=class{api;#e=null;constructor(){let e=wu();this.api=e?new km(e,Du()):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 tl||e instanceof $c){this.#e=e;return}throw e}}report(){if(this.#e){if(Y.blank(),this.#e instanceof tl){Y.warn(`Couldn't reach ${this.#e.url}. Run \`skilless sync\` to sync your skills with the cloud.`);return}Y.warn(`Your sign-in was rejected, so this only changed this machine.`),Y.dim("Run `skilless auth` to sign in again, then `skilless sync` to sync your skills.")}}};function jm(e){return e.includes(`/`)||e.includes(`:`)}function Mm(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 L(`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)?Nm(t,n):/^[a-z][a-z0-9+.-]*:\/\//i.test(t)||/^[^@/\s]+@[^:/\s]+:/.test(t)?{url:t,ref:n}:Pm(t,n),a.subpath&&=Fm(a.subpath),{...a,label:_l(a.url)??a.url}}function Nm(e,t){let n;try{n=new URL(e)}catch{throw new L(`${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 Pm(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 L(`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 Fm(e){let t=e.replace(/\\/g,`/`).split(`/`).filter(Boolean);if(t.includes(`..`))throw new L(`${e} reaches outside the repository.`);return t.join(`/`)}async function Im(e,t,r){let{dir:i,dispose:o}=cl(`skilless-`);try{let o=[`clone`,`--depth`,`1`,`--quiet`];e.ref&&o.push(`--branch`,e.ref),o.push(`--`,e.url,i);try{await J(`Cloning ${e.label}${e.ref?`#${e.ref}`:``}`,()=>Lm(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 Rm(e,t);z(i),a.mkdirSync(i);try{n(`git`,o,{stdio:[`inherit`,`ignore`,`pipe`],encoding:`utf8`})}catch(t){throw Rm(e,t)}}return await t(i)}finally{o()}}function Lm(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 Rm(e,t){let n=t.stderr?.trim();return new L(`Couldn't clone ${e.label}.`,{suggestion:n||`Check the address, and that you have access to the repository.`,cause:t})}const zm=new Set([`node_modules`,`.git`,`dist`,`build`,`__pycache__`]),Bm=[`skills`,`skills/.curated`,`skills/.experimental`,`skills/.system`,`.agents/skills`,`.claude/skills`];function Vm(e,t){let n=t?C.join(e,t):e;if(!R(n))throw new L(`${t} does not exist in the repository.`);let r=new Map,i=e=>{let t=Gm(e);r.has(t.name)||r.set(t.name,t)};if(Um(n))return i(n),[...r.values()];Wm(n,1,i);for(let e of Bm)Wm(C.join(n,e),3,i);return r.size===0&&Wm(n,5,i),[...r.values()].sort((e,t)=>e.name.localeCompare(t.name))}function Hm(e,t,n){try{let n=Fm(t);if(Um(C.join(e,n)))return{dir:C.join(e,n),path:n}}catch{}let r=Vm(e).find(e=>e.name===n);return r?{dir:r.dir,path:C.relative(e,r.dir)}:null}function Um(e){return a.existsSync(C.join(e,Wl))}function Wm(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()||zm.has(i.name))continue;let r=C.join(e,i.name);Um(r)?n(r):t>1&&Wm(r,t-1,n)}}function Gm(e){let t=Km(a.readFileSync(C.join(e,Wl),`utf8`)),n=t.name?.toLowerCase().replace(/[\s_]+/g,`-`);return{name:n&&Jl(n)?n:C.basename(e).toLowerCase(),dir:e,description:t.description}}function Km(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 qm=`skilless`,Jm=`Author skills once, sync them everywhere, install them per project.`;const X=`0.0.
|
|
126
|
-
`)}))}function $m(e){process.env[Zm]===`1`?console.trace(e):Y.error(G.default.red(e.toString())),process.exit(1)}async function $(e){try{return await e()}catch(e){return e instanceof L?$m(e):$m(new L(e instanceof Error?e.message:String(e),{suggestion:`Run with ${Zm}=1 for a stack trace.`,cause:e}))}}function eh(){let e=wu();if(!e)throw new $c;return new km(e,Du())}function th(e,t){if(t)return t;let n=vl(e);if(!n)throw new el;return n}const nh=`~/.agents/skills`;function rh(e){let t=C.normalize(s.homedir());return e===t||e.startsWith(`${t}/`)?`~${e.slice(t.length)}`:e}async function ih(e,t={}){for(let n of e.skipped){if(n.reason===`tracked`){Y.warn(`\`${n.name}\` is tracked by git here, so it was left alone.`);continue}let e=(n.occupied?.paths??[]).map(rh).join(` and `);if(!(t.yes||K&&await dm(`Overwrite ${e}?`,!1))){K?Y.dim(`Kept ${e}, so ${n.name} is not linked there.`):(Y.warn(`Skipped ${n.name}: ${e} was not made by skilless.`),Y.dim(` Run this in a terminal to overwrite it, or pass --yes.`));continue}vu(n,{copy:ju(t.copy)}),Y.step(`Overwrote ${e}.`)}for(let t of e.missing??[])Y.warn(`${t} is not on this machine yet, so it will be linked on the next sync.`)}const ah=Qm.extend({project:P().optional(),copy:I().optional(),global:I().optional(),notGlobal:I().optional(),overwrite:I(),yes:I()}),oh=new x(`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(Z.yes).addOption(Z.project).addOption(Z.cwd).action(async(e,t)=>{let n=Q(ah,t);Y.intro(X),await $(async()=>{let t=new Am,[r,...i]=e;if(r&&jm(r)){await lh(t,Mm(r),i,n);return}let a=e.find(jm);if(a)throw new L(`${a} is a repository, not a skill.`,{suggestion:`Name the repository first: \`skilless add ${a} <skills...>\`.`});let{entries:o,live:s}=await J(`Loading your library`,async()=>(await B(t),$u(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 L(`${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 L(`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){Y.info(r?`Every skill in your library is already global.`:`No skills in your library are global.`);return}if(i=await pm(r?`Make global`:`Stop being global`,e.map(e=>({name:e.name}))),i.length===0){Y.info(`Nothing selected.`);return}}r&&l(i),await sh(t,i,r,n),t.report();return}let f=th(n.cwd,n.project),p=new Set(await J(`Loading ${f}`,()=>ed(t,f))),m=e;if(m.length===0){if(o.filter(e=>!p.has(e.name)&&!e.global&&c(e)).length===0){Y.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 pm(`Add to ${f}`,o.filter(e=>e.global||p.has(e.name)||c(e)).map(e=>e.global?{name:e.name,disabled:`installed globally`}:p.has(e.name)?{name:e.name,disabled:`already added`}:{name:e.name})),m.length===0){Y.info(`Nothing selected.`);return}}let h=o.filter(e=>e.global).map(e=>e.name);l(m.filter(e=>!h.includes(e))),await ch(t,f,m,h,n),t.report()})});async function sh(e,t,n,r){Uu(t,n),await J(`Saving to skilless.dev`,()=>B(e));for(let e of t)Y.step(n?`${e} is now global, so every project has it.`:`${e} is no longer global.`);let i=r.project??vl(r.cwd),a=Cu(r.cwd),[o,s]=await J(`Updating your skills`,async()=>{if(!i){let t=await ad(e);return[t,t]}let t=await nd(e,a,i,{copy:r.copy});return[t,t.user]});for(let e of s.written.filter(e=>t.includes(e)))Y.dim(`Linked ${e} into ${nh}.`);for(let e of s.removed)Y.dim(`Unlinked ${e} from ${nh}.`);if(`project`in o){for(let e of o.project.removed)Y.dim(`Moved ${e} out of this project.`);for(let e of o.project.written.filter(e=>t.includes(e)))Y.dim(`${e} is still added to this project, so it is linked here again.`)}await ih(o,r)}async function ch(e,t,n,r,i){for(let e of n)r.includes(e)&&Y.dim(`${e} is global, so it is already in this project.`);let a=n.filter(e=>!r.includes(e));Vu(t,a);let o=Cu(i.cwd),s=await J(`Updating this project`,async()=>(await B(e),nd(e,o,t,{copy:i.copy})));for(let e of a)Y.step(`Added ${e} to this project.`);await ih(s,i)}async function lh(e,t,n,r){if(r.notGlobal)throw new L(`--not-global only applies to skills already in your library.`);let i=r.global?null:r.project??vl(r.cwd),a=await J(`Loading your library`,async()=>(await B(e),$u(e))),o=new Map(a.entries.map(e=>[e.name,e])),s=[...n,...t.skill?[t.skill]:[]],c=await Im(t,async e=>{let n=Vm(e,t.subpath);if(n.length===0)throw new L(`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 L(`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(!K)throw new L(`${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 pm(`Add from ${t.label}`,n.map(e=>({name:e.name,hint:uh(e.description)})));i=n.filter(t=>e.includes(t.name))}let a=[];for(let t of i)try{Yl(t.name),a.push({skill:Zl(t.dir,t.name),path:C.relative(e,t.dir)})}catch(e){Y.warn(`Skipped ${t.name}: ${e instanceof L?e.message:e}`)}return a},{interactive:K});if(c.length===0){Y.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?Xu(e.name,d.get(e.name)):l.push(e),u.push(e.name);continue}if(!(r.overwrite||K&&!r.yes&&await dm(`${e.name} is already in your library. Replace it with the one from ${t.label}?`))){Y.warn(`Kept your own ${e.name}. Pass --overwrite to replace it.`);continue}l.push(e),u.push(e.name)}let f=await J(`Saving ${l.length} skill(s) to your library`,()=>od(e,l,o,d));for(let e of f)Y.step(`Copied ${e} into your library from ${t.label}.`);if(u.length===0){e.report();return}r.global?await sh(e,u,!0,r):i?await ch(e,i,u,(await J(`Loading your library`,()=>$u(e))).entries.filter(e=>e.global).map(e=>e.name),r):(Y.blank(),Y.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 uh(e){if(e)return e.length>60?`${e.slice(0,57)}...`:e}function dh(e){switch(process.platform){case`darwin`:return[`open`,[e]];case`win32`:return[`rundll32`,[`url.dll,FileProtocolHandler`,e]];default:return[`xdg-open`,[e]]}}function fh(e){let[t,n]=dh(e);try{let e=r(t,n,{stdio:`ignore`,detached:!0,windowsHide:!0});e.on(`error`,()=>{}),e.unref()}catch{}}async function ph(){let e=c.randomBytes(16).toString(`hex`),t=Du(),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=f.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 L(`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}`;Y.info(`Opening your browser to sign in…`),Y.dim(r),Y.blank(),fh(r),Tu(await J(`Waiting for you to sign in`,()=>a))}finally{clearTimeout(s),o.close()}}const mh=Qm.extend({logout:I(),token:P().optional()}),hh=new x(`auth`).description(`Sign in to skilless.dev, or sign out.`).addOption(new S(`--logout`,`Sign out on this machine.`).default(!1)).addOption(new S(`--token <token>`,`Save a token directly, for when the browser flow cannot reach localhost.`)).addOption(Z.cwd).action(async e=>{let t=Q(mh,e);Y.intro(X),await $(async()=>{if(t.logout){if(!wu()){Y.info(`You were not signed in.`);return}Eu(),Y.step(`Signed out on this machine.`);return}if(t.token){Tu(t.token),Y.step(`Token saved. You are signed in to skilless.dev.`);return}await ph(),Y.step(`Signed in to skilless.dev.`)})}),gh=s.homedir(),_h=process.env.LOCALAPPDATA??C.join(gh,`AppData`,`Local`),vh=process.env.ProgramFiles??`C:/Program Files`,yh={cursor:{label:`Cursor`,cli:`cursor`,fallbacks:{darwin:[`/Applications/Cursor.app/Contents/Resources/app/bin/cursor`,`${gh}/Applications/Cursor.app/Contents/Resources/app/bin/cursor`],win32:[C.join(_h,`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`,`${gh}/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`],win32:[C.join(_h,`Programs/Microsoft VS Code/bin/code.cmd`),C.join(vh,`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`,`${gh}/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf`],win32:[C.join(_h,`Programs/Windsurf/bin/windsurf.cmd`)],linux:[`/usr/share/windsurf/bin/windsurf`]}},zed:{label:`Zed`,cli:`zed`,fallbacks:{darwin:[`/Applications/Zed.app/Contents/MacOS/cli`,`${gh}/Applications/Zed.app/Contents/MacOS/cli`],win32:[C.join(_h,`Programs/Zed/bin/zed.exe`)],linux:[`${gh}/.local/bin/zed`]}},sublime:{label:`Sublime Text`,cli:`subl`,fallbacks:{darwin:[`/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl`],win32:[C.join(vh,`Sublime Text/subl.exe`)],linux:[`/opt/sublime_text/sublime_text`]}},neovim:{label:`Neovim`,cli:`nvim`,terminal:!0},vim:{label:`Vim`,cli:`vim`,terminal:!0}},bh=Object.keys(yh);function xh(e){return Object.hasOwn(yh,e)}function Sh(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 Ch(e){let t=yh[e],n=Sh(t.cli);if(n)return n;let r=process.platform;return t.fallbacks?.[r]?.find(e=>R(e))??null}function wh(){return bh.filter(e=>Ch(e)!==null)}function Th(e){return yh[e].terminal===!0}function Eh(e,t){let n=Ch(e);if(!n)return Promise.reject(Error(`${yh[e].label} was not found. Is \`${yh[e].cli}\` on your PATH?`));let i=Th(e);return new Promise((e,a)=>{let o=r(n,[t],{cwd:C.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 Dh(){let e=dl(Nl,{});return e.editor!==void 0&&!xh(e.editor)&&delete e.editor,e}function Oh(e){fl(Nl,e)}function kh(e){let t={...Dh(),...e};for(let e of Object.keys(t))t[e]===void 0&&delete t[e];return Oh(t),t}const Ah=`none`,jh=Cc({unset:I()}),Mh=new x(`editor`).description("Choose the editor `skilless create` opens a new skill in.").argument(`[editor]`,`Set it directly: ${bh.join(`, `)}.`).addOption(new S(`--unset`,`Stop opening new skills in an editor.`).default(!1)).action(async(e,t)=>{let n=Q(jh,t);Y.intro(X),await $(async()=>{let t=Dh().editor;if(n.unset){kh({editor:void 0}),Y.step(`New skills will no longer open in an editor.`);return}if(e){if(!xh(e))throw new L(`${e} is not an editor skilless knows.`,{suggestion:`Pick one of: ${bh.join(`, `)}.`});kh({editor:e}),Y.step(`New skills will open in ${yh[e].label}.`);return}if(!K){Y.info(t?`Editor: ${yh[t].label}`:`No editor set.`);return}let r=wh();if(r.length===0)throw new L(`None of the editors skilless knows are installed.`,{suggestion:`skilless looks for: ${bh.map(e=>yh[e].cli).join(`, `)}.`});let i=await mm(`Open new skills in`,[...r.map(e=>({name:e,message:yh[e].label,hint:e===t?G.default.gray(`current`):void 0})),{name:Ah,message:`None`,hint:t?void 0:G.default.gray(`current`)}],t??Ah);if(i===Ah){kh({editor:void 0}),Y.step(`New skills will not open in an editor.`);return}xh(i)&&(kh({editor:i}),Y.step(`New skills will open in ${yh[i].label}.`))})}),Nh=new x(`config`).description(`View or change your skilless settings.`).addCommand(Mh).action(async()=>{Y.intro(X),await $(async()=>{let e=Dh();Y.info(`editor ${e.editor?yh[e.editor].label:G.default.gray(`not set`)}`),Y.blank(),Y.dim(`Stored in ${Nl}`)})}),Ph=Qm.extend({description:P().optional(),project:P().optional(),global:I().optional(),open:I()}),Fh=new x(`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(Z.project).addOption(Z.cwd).action(async(e,t)=>{let n=Q(Ph,t);Y.intro(X),await $(async()=>{let t=new Am,r=await J(`Loading your library`,async()=>(await B(t),$u(t))),i=new Set(r.entries.map(e=>e.name)),a=e;if(!a){if(!K)throw new L(`No name given.`,{suggestion:"Run `skilless create <name>`."});a=await fm(`Name`,{validate:e=>e?Jl(e)?i.has(e)?`${e} already exists in your library.`:!0:`Use lowercase letters, digits, dots, dashes and underscores.`:`A name is required.`})}if(Yl(a),tu(a))throw new L(`${a} already exists in your library.`,{suggestion:`Edit it at ${C.join(Fl(a),`SKILL.md`)}`});if(i.has(a))throw new L(`${a} already exists in your library.`,{suggestion:`Run \`skilless sync\` to bring ${a} down to this machine.`});let o=n.description??await fm(`What does this skill do?`);Ql(a,nu(a,o));let s=Zl(Fl(a),a);if(await J(`Creating ${a}`,()=>t.try(e=>e.putSkill(a,s.files,s.editedAt)))){let e=ku();e.skills[a]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:Date.now()},Au(e)}Y.step(`Created ${a} in your library.`),n.global&&Uu([a],!0);let c=n.project??vl(n.cwd);if(c){n.global||Vu(c,[a]);let e=Cu(n.cwd);await ih(await J(`Updating this project`,async()=>(await B(t),nd(t,e,c))))}else n.global?await ih(await J(`Linking your global skills`,async()=>(await B(t),ad(t)))):await J(`Saving to skilless.dev`,()=>B(t));n.global?Y.step(`${a} is now global, so every project has it.`):c?Y.step(`Added ${a} to ${c}.`):Y.warn(`No git remote here, so ${a} was not added to a project.`);let l=C.join(Fl(a),`SKILL.md`),u=n.open?Dh().editor:void 0,d=u&&(!Th(u)||K);if(Y.blank(),u&&d)try{Y.dim(`Opening ${a} in ${yh[u].label}...`),await Eh(u,l)}catch(e){Y.warn(e instanceof Error?e.message:String(e)),Y.dim(`Edit ${a} at ${l}`)}else Y.dim(`Edit ${a} at ${l}`);t.api&&Y.dim("Run `skilless sync` to save your changes."),t.report()})}),Ih=Qm.extend({yes:I()}),Lh=new x(`delete`).description(`Delete skills from your library, everywhere.`).argument(`<skills...>`,`Skills to delete.`).addOption(Z.yes).addOption(Z.cwd).action(async(e,t)=>{let n=Q(Ih,t);Y.intro(X),await $(async()=>{let t=new Am,r=await J(`Loading your library`,async()=>(await B(t),$u(t))),i=new Set(r.entries.map(e=>e.name)),a=e.filter(e=>!i.has(e));if(a.length>0)throw new L(`Not in your library: ${a.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});if(Y.warn(`This deletes ${e.join(`, `)} from every project, on every machine.`),Y.dim(`Run \`skilless remove ${e.join(` `)}\` to take ${e.length===1?`it`:`them`} out of just this project.`),!(n.yes||await dm(`Delete ${e.join(`, `)}?`,!1))){Y.info(`Nothing was deleted.`);return}Gu(e),Zu(e);let o=ku(),s=Cu(n.cwd);for(let t of e)z(Fl(t)),delete o.skills[t],Y.step(`Deleted ${t} from your library.`);Au(o),xu(s,e),xu(iu(),e),await J(`Deleting from skilless.dev`,()=>B(t)),Y.blank(),Y.dim("Other checkouts will clean up on their next `skilless install`."),Y.dim(`Restore at skilless.dev/skills within 30 days.`),t.report()})}),Rh=Qm.extend({yes:I(),overwrite:I()});function zh(e){return a.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(t=>({name:t.name,dir:C.join(e,t.name)})).filter(e=>R(C.join(e.dir,Wl))).sort((e,t)=>e.name.localeCompare(t.name))}const Bh=new x(`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(Z.yes).addOption(Z.cwd).action(async(e,t)=>{let n=Q(Rh,t);Y.intro(X),await $(async()=>{let t=new Am,r=C.resolve(n.cwd,e);if(!R(r))throw new L(`${r} does not exist.`);let i=zh(r);if(i.length===0)throw new L(`No skills found in ${r}.`,{suggestion:`A skill is a directory containing a ${Wl}.`});let a=await J(`Loading your library`,async()=>(await B(t),$u(t))),o=new Map(a.entries.map(e=>[e.name,e])),s=[],c=[];for(let e of i){if(!Jl(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(Zl(e.dir,e.name))}catch(t){c.push({name:e.name,reason:t instanceof Error?t.message:`unreadable`})}}for(let e of c)Y.warn(`Skipped ${e.name}: ${e.reason}.`);if(s.length===0){Y.info(`Nothing to import.`),t.report();return}if(Y.blank(),Y.info(`Importing: ${s.map(e=>e.name).join(`, `)}`),!(n.yes||await dm(`Import ${s.length} skill(s)?`,!0)))return;let l=await J(`Importing ${s.length} skill(s)`,()=>od(t,s,o));for(let e of l)Y.step(`Imported ${e} into your library.`);Y.blank(),Y.dim(`Run \`skilless add ${s[0]?.name??`<skill>`}\` to add one to this project.`),t.report()})}),Vh=new x(`init`).description(`Create the local skilless directory.`).addOption(Z.cwd).action(async e=>{Q(Qm,e),Y.intro(X),await $(async()=>{let e=!R(Dl);rl(Dl),Y.step(`${e?`Created`:`Found`} your library at ${El}.`),Y.blank(),Y.dim("Run `skilless create <name>` to make a skill."),Y.dim("Run `skilless import <dir>` to bring in skills you already have."),wu()||Y.dim("Run `skilless auth` to sync your skills with the cloud.")})}),Hh=Qm.extend({project:P().optional(),copy:I().optional()}),Uh=new x(`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(Z.project).addOption(Z.cwd).action(async e=>{let t=Q(Hh,e);Y.intro(X),await $(async()=>{let e=eh(),n=th(t.cwd,t.project),r=Cu(t.cwd),i=await J(`Installing ${n}'s skills`,async()=>(await Ku(e),Pu(e,r,n,{copy:t.copy}))),{project:a,user:o}=i;for(let e of[...a.pruned,...o.pruned])Y.dim(`Pruned ${e}, which is no longer in your library.`);for(let e of[...a.fetched,...o.fetched])Y.dim(`Fetched ${e} from your library.`);for(let e of a.removed)Y.dim(`Removed ${e}, which is no longer in this project.`);for(let e of o.removed)Y.dim(`Removed ${e} from ${nh}, since it is no longer global.`);for(let e of a.written)Y.step(`Installed ${e}.`);for(let e of o.written)Y.step(`Installed ${e} globally.`);if(i.count===0){Y.info(`No skills added to ${n} yet.`),Y.dim("Run `skilless add <skill>` to add one.");return}await ih(i,{copy:t.copy}),Y.blank(),Y.dim(ju(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.`)})}),Wh=Qm.extend({project:Tc([I(),P()]).optional()});function Gh(e,t){let n=[];return e.global&&n.push(`global`),e.source&&n.push(`from ${_l(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 Kh=new x(`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(Z.cwd).action(async e=>{let t=Q(Wh,e);Y.intro(X),await $(async()=>{let e=new Am,n=await J(`Loading your library`,async()=>(await B(e),$u(e)));if(t.project!==void 0){let r=th(t.cwd,typeof t.project==`string`?t.project:void 0),i=td(n,await J(`Loading ${r}`,()=>ed(e,r)));Y.info(`${G.default.bold(r)}`),Y.blank(),i.length===0&&Y.dim("No skills in this project. Run `skilless add <skill>` to add one.");for(let t of i)Y.dim(` ${t.name}${G.default.gray(Gh(t,e.api!==null))}`);e.report();return}n.entries.length===0&&Y.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);Y.dim(` ${t.name.padEnd(28)} ${G.default.gray(r+Gh(t,e.api!==null))}`)}e.report()})});function qh(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 Jh(e,t,n){return Yh(e,t===`push`?`remote`:`local`,n)}function Yh(e,t,n){let r=new Date().toISOString().replace(/[:.]/g,`-`),i=C.join(Pl,e,`${r}-${t}`);for(let e of n)ul(C.join(i,e.path),e.contents);return i}async function Xh(e,t,n){let r=Array(e.length),i=0;return await Promise.all(Array.from({length:Math.min(t,e.length)},async()=>{for(;i<e.length;){let t=i++;r[t]=await n(e[t])}})),r}async function Zh(e,t,n){let r={actions:[],conflicts:[],skipped:[]},i=Date.now(),a=new Set;for(let e of t)e.type!==`delete-local`&&e.type!==`delete-remote`||(n.yes||await dm(e.type===`delete-local`?`Delete ${e.name} from this machine? It was deleted elsewhere.`:`Delete ${e.name} everywhere? It was deleted on this machine.`,!0)?a.add(e):r.skipped.push(e.name));let o=await Xh(t,8,async t=>{let{name:o}=t,s=n.local.get(o);switch(t.type){case`noop`:return s&&(n.state.skills[o]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:i}),!0;case`push`:case`conflict-push`:if(!s)return!0;if(t.type===`conflict-push`){let t=await e.getSkill(o);t&&r.conflicts.push({name:o,kept:`push`,stashedAt:Jh(o,`push`,t.files)})}return n.remote.set(o,await e.putSkill(o,s.files,s.editedAt)),n.state.skills[o]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:i},!0;case`pull`:case`conflict-pull`:{let a=await e.getSkill(o);return a?(t.type===`conflict-pull`&&s&&r.conflicts.push({name:o,kept:`pull`,stashedAt:Jh(o,`pull`,s.files)}),Ql(o,a.files,a.editedAt),n.state.skills[o]={contentHash:a.contentHash,editedAt:a.editedAt,syncedAt:i},!0):!0}case`delete-local`:return a.has(t)?(z(Fl(o)),delete n.state.skills[o],!0):!1;case`delete-remote`:return a.has(t)?(await e.deleteSkill(o),n.remote.delete(o),delete n.state.skills[o],!0):!1}});return r.actions=t.filter((e,t)=>o[t]),r.conflicts.sort((e,t)=>e.name.localeCompare(t.name)),r}function Qh(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 $h=Qm.extend({yes:I(),user:I().optional(),includeCommitted:I().optional()});function eg(e){let t=Math.max(0,Math.round((Date.now()-e)/6e4));if(t<1)return`just now`;if(t<60)return`${t} min ago`;let n=Math.round(t/60);if(n<24)return`${n} hour${n===1?``:`s`} ago`;let r=Math.round(n/24);return`${r} day${r===1?``:`s`} ago`}function tg(e,t){return R(e)?a.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.isSymbolicLink()).map(n=>({name:n.name,dir:C.join(e,n.name),scope:t})).filter(e=>R(C.join(e.dir,Wl))):[]}async function ng(e,t){if(t.includeCommitted)return`move`;if(t.yes||!K)return Y.warn(`Skipped ${e}: it is committed to this repo. Pass --include-committed to move it out of git.`),`leave`;Y.warn(`${e} is committed to this repo.`),Y.note(`Moving it untracks it from git, so teammates lose it on their next pull unless they use skilless.`);let n=await mm(`What should happen to ${e}?`,[{name:`leave`,message:`Leave it`},{name:`copy`,message:`Copy it to my library`,hint:`keep it committed`},{name:`move`,message:`Move it`,hint:`untrack it from git`}]);return n===`copy`||n===`move`?n:`leave`}const rg=new x(`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.`).argument(`[skills...]`,`Skills to migrate, by name. Omit to migrate everything found.`).option(`--user`,`Migrate your user-level skills too, without asking.`).option(`--include-committed`,`Move skills committed to this repo too, without asking. Their removal from git is staged, not committed.`).addOption(Z.yes).addOption(Z.cwd).action(async(e,t)=>{let n=Q($h,t);Y.intro(X),await $(async()=>{let t=new Am,r=Cu(n.cwd),i=vl(n.cwd),a=t=>e.length===0||e.includes(t.name),o=(i?[Il,Ll].flatMap(e=>tg(C.join(r,e),`project`)):[]).filter(a),s=[...tg(Bl(),`user`),...tg(zl(),`user`)].filter(a),c=e.filter(e=>![...o,...s].some(t=>t.name===e));if(c.length>0)throw new L(`Nothing to migrate named ${c.join(`, `)}.`,{suggestion:i?`Migrate looks for real skill directories in ${Il}, ${Ll} and at the user level. Linked ones are already in skilless.`:`This directory has no git remote, so only user-level skills can be migrated from it.`});let l=e=>xl(r,C.relative(r,e.dir)),u=[...new Set(o.filter(l).map(e=>e.name))].sort(),d=new Map;for(let e of u)d.set(e,await ng(e,n));let f=[],p=[];for(let e of o){let t=d.get(e.name);t?t===`copy`?p.push({...e,committed:`copy`}):t===`move`&&p.push(l(e)?{...e,committed:`move`}:e):f.push(e)}if(i||Y.dim(`No git remote here, so there are no project skills to migrate.`),f.length===0&&p.length===0&&s.length===0){Y.info(`Nothing to migrate.`);return}let m=await J(`Loading your library`,async()=>(await B(t),$u(t))),h=new Map(m.entries.map(e=>[e.name,e])),g=new Map,_=async(e,r)=>{let{name:i}=e,a=h.get(i),o=g.get(i);if(!o){if(n.yes||!K)Y.warn(`Skipped ${i}: a different ${i} is already in your library. Run migrate in a terminal to pick one.`),o=`skip`;else{let t=a.local?.editedAt??a.remote?.editedAt;Y.warn(`A different ${i} is already in your library.`),Y.note(`On disk: edited ${eg(e.editedAt)}, in ${r.map(e=>rh(e.dir)).join(` and `)}`),Y.note(`In your library: edited ${t?eg(t):`at an unknown time`}`);let n=await mm(`Which ${i} do you want to keep?`,[{name:`library`,message:`The one in my library`,hint:`replaces the copy on disk`},{name:`local`,message:`The one on disk`,hint:`replaces it in your library, everywhere`},{name:`skip`,message:`Skip it for now`}]);o=n===`library`||n===`local`?n:`skip`}g.set(i,o)}if(o===`skip`)return null;let s=a.local?.files??(await J(`Fetching ${i} from your library`,()=>t.try(e=>e.getSkill(i))))?.files;if(!s)return Y.warn(`Skipped ${i}: could not fetch the one in your library.`),null;if(o===`library`){let t=a.local?.editedAt??a.remote.editedAt;return{skill:{...e,files:s,editedAt:t,contentHash:a.local?.contentHash??a.remote.contentHash},adopted:!0,stash:{label:`local`,files:e.files}}}return{skill:{...e,editedAt:Date.now()},adopted:!1,stash:{label:`library`,files:s}}},v=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(!Jl(e)){Y.warn(`Skipped ${e}: not a valid skill name.`);continue}let t;try{t=r.map(t=>Zl(t.dir,e))}catch(t){Y.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)){Y.warn(`Skipped ${e}: found more than one version (${r.map(e=>e.dir).join(`, `)}).`);continue}let a=h.get(e),o=a?.local?.contentHash??a?.remote?.contentHash,s=o&&o!==i.contentHash?await _(i,r):{skill:i,adopted:o!==void 0};s&&n.push({...s,dirs:r.filter(e=>e.committed!==`copy`).map(e=>e.dir),untrack:r.filter(e=>e.committed===`move`).map(e=>e.dir),copyOnly:r.every(e=>e.committed===`copy`),scope:r.some(e=>e.scope===`user`)?`user`:`project`})}return n},y=await v(f);if(y.length>0&&(Y.list(`Project skills in ${i}`,y.map(e=>e.skill.name)),n.yes||e.length>0||await dm(y.length===1?`Move it into skilless and link it here?`:`Move these ${y.length} into skilless and link them here?`,!0)||(y=[])),y.push(...await v(p)),s.length>0){let t=[...new Set(s.map(e=>e.name))].sort();Y.list(`User-level skills`,t);let r=n.user===!0||e.length>0;if(!r&&!n.yes&&K&&(Y.note(`They would become global skills, linked back into ~/.agents/skills so every project still gets them.`),r=await dm(t.length===1?`Migrate it too?`:`Migrate these ${t.length} too?`,!1)),r){let e=new Set(y.map(e=>e.skill.name)),t=await v([...s,...[...f,...p].filter(e=>s.some(t=>t.name===e.name))]);y=[...y.filter(e=>!t.some(t=>t.skill.name===e.skill.name)),...t];for(let t of e)y.some(e=>e.skill.name===t)||Y.warn(`Skipped ${t}: its project and user-level copies differ.`)}}if(y.length===0){Y.info(`Nothing was migrated.`);return}let ee=ku(),te=[];for(let{skill:e,adopted:n,stash:r}of y)r&&te.push(Yh(e.name,r.label,r.files)),Ql(e.name,e.files,e.editedAt),!n&&(await J(`Saving ${e.name} to your library`,()=>t.try(t=>t.putSkill(e.name,e.files,e.editedAt)))?ee.skills[e.name]={contentHash:e.contentHash,editedAt:e.editedAt,syncedAt:Date.now()}:delete ee.skills[e.name]);Au(ee);let b=[];for(let{dirs:e,untrack:t}of y)for(let n of e){let e=C.relative(r,n);if(t.includes(n)){if(!Sl(r,e)){Y.warn(`Could not untrack ${e} from git, so it was left in place.`);continue}b.push(e)}z(n)}let ne=y.filter(e=>e.scope===`user`).map(e=>e.skill.name),re=y.filter(e=>e.scope===`project`&&!e.copyOnly).map(e=>e.skill.name);ne.length>0&&Uu(ne,!0),i&&re.length>0&&Vu(i,re),await J(`Saving to skilless.dev`,()=>B(t));let ie=e=>y.filter(e).map(e=>e.skill.name),ae=[[`Moved into your library and this project`,ie(e=>re.includes(e.skill.name)&&!e.adopted)],[`Already in your library, now linked to this project`,ie(e=>re.includes(e.skill.name)&&e.adopted)],[`Moved into your library as global skills`,ie(e=>e.scope===`user`&&!e.adopted)],[`Already in your library, now linked as global skills`,ie(e=>e.scope===`user`&&e.adopted)],[`Copied into your library, still committed here`,ie(e=>e.copyOnly&&!e.adopted)],[`Already in your library, still committed here`,ie(e=>e.copyOnly&&e.adopted)]];for(let[e,t]of ae)t.length>0&&Y.list(e,t,!0);if(te.length>0){Y.note(`The copies you did not keep are saved here:`);for(let e of te)Y.dim(` ${rh(e)}`)}b.length>0&&Y.note(`Staged the removal of ${b.join(`, `)} from git. Commit it when you are ready.`),i?await ih(await J(`Updating this project`,()=>nd(t,r,i)),{yes:n.yes}):ne.length>0&&await ih(await J(`Linking your global skills`,()=>ad(t)),{yes:n.yes}),t.report()})}),ig=Qm.extend({project:P().optional(),copy:I().optional()}),ag=new x(`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(Z.project).addOption(Z.cwd).action(async(e,t)=>{let n=Q(ig,t);Y.intro(X),await $(async()=>{let t=new Am,r=th(n.cwd,n.project),[i,a]=await J(`Loading your library`,async()=>(await B(t),[await $u(t),new Set(await ed(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)){Y.warn(`${t} is global, so it is in every project.`),Y.dim(` Run \`skilless add --not-global ${t}\` to take it out of every project.`);continue}if(!a.has(t)){Y.warn(`${t} is not in this project.`);continue}s.push(t)}if(s.length===0){t.report();return}Hu(r,s);let c=Cu(n.cwd),l=await J(`Updating this project`,async()=>(await B(t),nd(t,c,r,{copy:n.copy})));for(let e of s)Y.step(`Removed ${e} from this project.`);await ih(l,{copy:n.copy}),Y.blank(),Y.dim(`Run \`skilless delete ${s.join(` `)}\` to delete ${s.length===1?`it`:`them`}.`),t.report()})}),og=Qm.extend({yes:I(),push:I().optional(),pull:I().optional()}),sg=new x(`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(Z.yes).addOption(Z.cwd).action(async e=>{let t=Q(og,e);Y.intro(X),await $(async()=>{let e=eh(),[n,r]=await J(`Checking skilless.dev`,async()=>{let t=await e.listSkills(),n=await Ku(e,t);return[n,n>0?await e.listSkills():t]}),i=new Map(eu().map(e=>[e.name,e])),a=new Map(r.map(e=>[e.name,e])),o=ku(),s=qh(i,a,o,t.push?`push`:t.pull?`pull`:void 0),c=await J(`Syncing your library`,()=>Zh(e,s,{local:i,remote:a,state:o,yes:t.yes}));Au(o);let l=c.actions.map(Qh).filter(e=>e!==null);for(let e of l)Y.step(e);let[u,d]=await J(`Syncing your library`,async()=>{let t=[...a.values()],r=await Ku(e,t);return[n+r,r>0?await e.listSkills():t]});u>0&&Y.step(`Sent ${u} change(s) made while offline.`),Hl(d),Qu(d);let f=vl(t.cwd),p=await J(f?`Updating this project`:`Updating your global skills`,async()=>{if(!f)return{user:Iu(d.filter(e=>e.global).map(e=>e.name))};let n=await Pu(e,Cu(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)Y.step(`Linked ${e} into this project.`);for(let e of m.removed)Y.step(`Unlinked ${e}, which is no longer in this project.`);for(let e of m.pruned)Y.step(`Unlinked ${e}, which is no longer in your library.`)}for(let e of h.added)Y.step(`Linked ${e} into ${nh}.`);for(let e of h.removed)Y.step(`Unlinked ${e} from ${nh}, since it is no longer global.`);for(let e of h.pruned)Y.step(`Unlinked ${e} from ${nh}, 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&&Y.info(`Everything is already in sync.`),c.conflicts.length>0){Y.blank(),Y.warn(`${c.conflicts.length} skill(s) changed in two places. The copy that lost is kept here:`);for(let e of c.conflicts)Y.dim(` ${e.stashedAt}`)}await ih({skipped:[...m?.skipped??[],...h.skipped]},{yes:t.yes}),c.skipped.length>0&&(Y.blank(),Y.dim(`Skipped ${c.skipped.join(`, `)}.`))})}),cg=Qm.extend({yes:I(),force:I()});function lg(e){let t=_l(e.url)??e.url;return e.ref?`${t}#${e.ref}`:t}const ug=new x(`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(Z.yes).addOption(Z.cwd).action(async(e,t)=>{let n=Q(cg,t);Y.intro(X),await $(async()=>{let t=new Am,r=await J(`Loading your library`,async()=>(await B(t),$u(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 L(`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 L(`${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){Y.info(`None of your skills were added from a repository.`),Y.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=lg(r);await Im({url:r.url,ref:r.ref,label:i},async r=>{for(let a of e){let e=Hm(r,a.source.path,a.name);if(!e){Y.warn(`${a.name} is no longer in ${i}.`);continue}let o;try{o=Zl(e.dir,a.name)}catch(e){Y.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){Xu(a.name,s),u.push(a.name);continue}if(p!==a.source.hash){let e=n.force===!0;if(!e&&K&&!n.yes&&(Y.note(`You have edited ${a.name} since it came from ${i}.`),e=await dm(`Replace your edits to ${a.name}?`)),!e){d.push(a.name);continue}let r=a.local?.files??(await J(`Fetching your copy of ${a.name}`,()=>t.try(e=>e.getSkill(a.name))))?.files;if(!r){Y.warn(`Couldn't fetch your copy of ${a.name} to keep it, so it was left alone.`);continue}f.push({name:a.name,dir:Yh(a.name,`update`,r)})}c.push(o),l.set(a.name,s)}},{interactive:K}).then(()=>!0,e=>{if(!(e instanceof L))throw e;return Y.warn(e.toString()),!1})||p.push(...e.map(e=>e.name))}Y.blank();let m=await J(`Saving ${c.length} skill(s) to your library`,()=>od(t,c,i,l));for(let e of m)Y.step(`Updated ${e}.`);if(u.length>0&&Y.info(u.length===1?`${u[0]} is already up to date.`:`${u.length} skills are already up to date.`),d.length>0&&Y.warn(`Kept your edits to ${d.join(`, `)}. Run \`skilless update ${d.join(` `)} --force\` to replace them.`),p.length>0&&Y.warn(`Couldn't check ${p.join(`, `)}.`),f.length>0){Y.blank(),Y.dim(`Your edited copies are kept here:`);for(let e of f)Y.dim(` ${e.dir}`)}t.report()})}),dg=Qm.extend({project:P().optional(),yes:I()}),fg=new x(`vendor`).description(`Copy skills into this repo as real files to commit, instead of linking them.`).argument(`[skills...]`,`Skills to vendor. Omit to pick from a list.`).addOption(Z.yes).addOption(Z.project).addOption(Z.cwd).action(async(e,t)=>{let n=Q(dg,t);Y.intro(X),await $(async()=>{let t=hl(n.cwd);if(!t)throw new L(`This directory is not in a git repo.`,{suggestion:`Vendored skills are meant to be committed, so run this inside one.`});let r=new Am,i=n.project??vl(n.cwd),[a,o]=await J(`Loading your library`,async()=>(await B(r),[await $u(r),new Set(i?await ed(r,i):[])])),s=new Map(a.entries.map(e=>[e.name,e])),c=e.filter(e=>!s.has(e));if(c.length>0)throw new L(`Not in your library: ${c.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});let l=e;if(l.length===0){if(a.entries.length===0){Y.info(`Your library is empty.`),r.report();return}if(l=await pm(`Vendor into this repo`,a.entries.map(e=>({name:e.name,...o.has(e.name)?{hint:`in this project`}:{}}))),l.length===0){Y.info(`Nothing selected.`);return}}let u=[];for(let e of l){let i=await J(`Reading ${e}`,()=>pg(r,s.get(e)));if(!i){Y.warn(`${e} is not on this machine, and the server can't be reached to fetch it.`);continue}let a=bu(t,e);if(a.length>0){let t=a.join(` and `);if(!(n.yes||K&&await dm(`Overwrite ${t}?`,!1))){Y.warn(`Skipped ${e}: ${t} was not made by skilless.`),K||Y.dim(` Run this in a terminal to overwrite it, or pass --yes.`);continue}}yu(t,{name:e,files:i}),u.push(e),Y.step(`Vendored ${e} into ${Il}/${e}.`)}if(u.length===0){r.report();return}let d=i?u.filter(e=>o.has(e)):[];if(i&&d.length>0){Hu(i,d),await J(`Updating this project`,()=>B(r));for(let e of d)Y.dim(`Took ${e} out of this project in skilless.`)}for(let e of u.filter(e=>s.get(e).global))Y.dim(`${e} is global, so it is also still linked at the user level.`);Y.blank(),Y.dim(`Commit ${u.flatMap(e=>[`${Il}/${e}`,`${Ll}/${e}`]).join(`, `)} to share ${u.length===1?`it`:`them`}.`),r.report()})});async function pg(e,t){return t.local?t.local.files:(await e.try(e=>e.getSkill(t.name)))?.files??null}ce.name(Ym).description(Xm).version(X).addCommand(Vh).addCommand(hh).addCommand(Fh).addCommand(oh).addCommand(ug).addCommand(ag).addCommand(Lh).addCommand(Uh).addCommand(fg).addCommand(sg).addCommand(Bh).addCommand(rg).addCommand(Kh).addCommand(Nh).parse();export{};
|
|
125
|
+
`)}},xm=Cc({path:P(),contents:P()}),Sm=Cc({url:P(),ref:P().optional(),path:P(),hash:P()}),Cm=Cc({name:P(),contentHash:P(),editedAt:fc(),updatedAt:fc(),global:I(),source:Sm.nullable().default(null)}),wm=Cm.extend({files:xc(xm)}),Tm=Cc({bound:xc(P()),unknown:xc(P())}),Em=Cc({error:P(),message:P().optional()}),Dm=new Set([502,503,504]);function Om(e){try{return JSON.parse(e)}catch{return null}}var km=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 tl(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(Dm.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 tl(this.#t,r)}async#i(e,t,n,r){let i=await this.#r(e,t,r);if(i.status===401)throw new $c;if(i.status<200||i.status>=300){let n=Em.safeParse(Om(i.text));throw new L(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(Om(i.text));if(!a.success)throw new L(`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`,xc(Cm))}async getSkill(e){return await this.#i(`GET`,`/skills/${encodeURIComponent(e)}`,wm.nullable())}async putSkill(e,t,n){return await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}`,Cm,{files:t,editedAt:n})}async setGlobal(e,t){await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}/global`,_c(),{global:t})}async setSource(e,t){await this.#i(`PUT`,`/skills/${encodeURIComponent(e)}/source`,_c(),{source:t})}async deleteSkill(e){await this.#i(`DELETE`,`/skills/${encodeURIComponent(e)}`,_c())}async getBindings(e){return await this.#i(`GET`,`/projects/skills?key=${encodeURIComponent(e)}`,xc(wm))}async setBindings(e,t){return await this.#i(`PUT`,`/projects/skills`,Tm,{key:e,names:t})}},Am=class{api;#e=null;constructor(){let e=wu();this.api=e?new km(e,Du()):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 tl||e instanceof $c){this.#e=e;return}throw e}}report(){if(this.#e){if(Y.blank(),this.#e instanceof tl){Y.warn(`Couldn't reach ${this.#e.url}. Run \`skilless sync\` to sync your skills with the cloud.`);return}Y.warn(`Your sign-in was rejected, so this only changed this machine.`),Y.dim("Run `skilless auth` to sign in again, then `skilless sync` to sync your skills.")}}};function jm(e){return e.includes(`/`)||e.includes(`:`)}function Mm(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 L(`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)?Nm(t,n):/^[a-z][a-z0-9+.-]*:\/\//i.test(t)||/^[^@/\s]+@[^:/\s]+:/.test(t)?{url:t,ref:n}:Pm(t,n),a.subpath&&=Fm(a.subpath),{...a,label:_l(a.url)??a.url}}function Nm(e,t){let n;try{n=new URL(e)}catch{throw new L(`${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 Pm(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 L(`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 Fm(e){let t=e.replace(/\\/g,`/`).split(`/`).filter(Boolean);if(t.includes(`..`))throw new L(`${e} reaches outside the repository.`);return t.join(`/`)}async function Im(e,t,r){let{dir:i,dispose:o}=cl(`skilless-`);try{let o=[`clone`,`--depth`,`1`,`--quiet`];e.ref&&o.push(`--branch`,e.ref),o.push(`--`,e.url,i);try{await J(`Cloning ${e.label}${e.ref?`#${e.ref}`:``}`,()=>Lm(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 Rm(e,t);z(i),a.mkdirSync(i);try{n(`git`,o,{stdio:[`inherit`,`ignore`,`pipe`],encoding:`utf8`})}catch(t){throw Rm(e,t)}}return await t(i)}finally{o()}}function Lm(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 Rm(e,t){let n=t.stderr?.trim();return new L(`Couldn't clone ${e.label}.`,{suggestion:n||`Check the address, and that you have access to the repository.`,cause:t})}const zm=new Set([`node_modules`,`.git`,`dist`,`build`,`__pycache__`]),Bm=[`skills`,`skills/.curated`,`skills/.experimental`,`skills/.system`,`.agents/skills`,`.claude/skills`];function Vm(e,t){let n=t?C.join(e,t):e;if(!R(n))throw new L(`${t} does not exist in the repository.`);let r=new Map,i=e=>{let t=Gm(e);r.has(t.name)||r.set(t.name,t)};if(Um(n))return i(n),[...r.values()];Wm(n,1,i);for(let e of Bm)Wm(C.join(n,e),3,i);return r.size===0&&Wm(n,5,i),[...r.values()].sort((e,t)=>e.name.localeCompare(t.name))}function Hm(e,t,n){try{let n=Fm(t);if(Um(C.join(e,n)))return{dir:C.join(e,n),path:n}}catch{}let r=Vm(e).find(e=>e.name===n);return r?{dir:r.dir,path:C.relative(e,r.dir)}:null}function Um(e){return a.existsSync(C.join(e,Wl))}function Wm(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()||zm.has(i.name))continue;let r=C.join(e,i.name);Um(r)?n(r):t>1&&Wm(r,t-1,n)}}function Gm(e){let t=Km(a.readFileSync(C.join(e,Wl),`utf8`)),n=t.name?.toLowerCase().replace(/[\s_]+/g,`-`);return{name:n&&Jl(n)?n:C.basename(e).toLowerCase(),dir:e,description:t.description}}function Km(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 qm=`skilless`,Jm=`Author skills once, sync them everywhere, install them per project.`;const X=`0.0.9`,Ym=qm,Xm=Jm,Zm=`SKILLESS_TRACE`,Qm=Cc({cwd:P()}),Z={cwd:new S(`--cwd <path>`,`The current working directory.`).default(process.cwd()),yes:new S(`-y, --yes`,`Skip confirmation prompts.`).default(!1),project:new S(`--project <key>`,`Override the project key. Defaults to the normalized git remote.`)};function Q(e,t){let n=e.safeParse(t);return n.success?n.data:$m(new L(`Invalid options.`,{suggestion:n.error.issues.map(e=>` ${e.message}`).join(`
|
|
126
|
+
`)}))}function $m(e){process.env[Zm]===`1`?console.trace(e):Y.error(G.default.red(e.toString())),process.exit(1)}async function $(e){try{return await e()}catch(e){return e instanceof L?$m(e):$m(new L(e instanceof Error?e.message:String(e),{suggestion:`Run with ${Zm}=1 for a stack trace.`,cause:e}))}}function eh(){let e=wu();if(!e)throw new $c;return new km(e,Du())}function th(e,t){if(t)return t;let n=vl(e);if(!n)throw new el;return n}const nh=`~/.agents/skills`;function rh(e){let t=C.normalize(s.homedir());return e===t||e.startsWith(`${t}/`)?`~${e.slice(t.length)}`:e}async function ih(e,t={}){for(let n of e.skipped){if(n.reason===`tracked`){Y.warn(`\`${n.name}\` is tracked by git here, so it was left alone.`);continue}let e=(n.occupied?.paths??[]).map(rh).join(` and `);if(!(t.yes||K&&await dm(`Overwrite ${e}?`,!1))){K?Y.dim(`Kept ${e}, so ${n.name} is not linked there.`):(Y.warn(`Skipped ${n.name}: ${e} was not made by skilless.`),Y.dim(` Run this in a terminal to overwrite it, or pass --yes.`));continue}vu(n,{copy:ju(t.copy)}),Y.step(`Overwrote ${e}.`)}for(let t of e.missing??[])Y.warn(`${t} is not on this machine yet, so it will be linked on the next sync.`)}const ah=Qm.extend({project:P().optional(),copy:I().optional(),global:I().optional(),notGlobal:I().optional(),overwrite:I(),yes:I()}),oh=new x(`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(Z.yes).addOption(Z.project).addOption(Z.cwd).action(async(e,t)=>{let n=Q(ah,t);Y.intro(X),await $(async()=>{let t=new Am,[r,...i]=e;if(r&&jm(r)){await lh(t,Mm(r),i,n);return}let a=e.find(jm);if(a)throw new L(`${a} is a repository, not a skill.`,{suggestion:`Name the repository first: \`skilless add ${a} <skills...>\`.`});let{entries:o,live:s}=await J(`Loading your library`,async()=>(await B(t),$u(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 L(`${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 L(`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){Y.info(r?`Every skill in your library is already global.`:`No skills in your library are global.`);return}if(i=await pm(r?`Make global`:`Stop being global`,e.map(e=>({name:e.name}))),i.length===0){Y.info(`Nothing selected.`);return}}r&&l(i),await sh(t,i,r,n),t.report();return}let f=th(n.cwd,n.project),p=new Set(await J(`Loading ${f}`,()=>ed(t,f))),m=e;if(m.length===0){if(o.filter(e=>!p.has(e.name)&&!e.global&&c(e)).length===0){Y.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 pm(`Add to ${f}`,o.filter(e=>e.global||p.has(e.name)||c(e)).map(e=>e.global?{name:e.name,disabled:`installed globally`}:p.has(e.name)?{name:e.name,disabled:`already added`}:{name:e.name})),m.length===0){Y.info(`Nothing selected.`);return}}let h=o.filter(e=>e.global).map(e=>e.name);l(m.filter(e=>!h.includes(e))),await ch(t,f,m,h,n),t.report()})});async function sh(e,t,n,r){Uu(t,n),await J(`Saving to skilless.dev`,()=>B(e));for(let e of t)Y.step(n?`${e} is now global, so every project has it.`:`${e} is no longer global.`);let i=r.project??vl(r.cwd),a=Cu(r.cwd),[o,s]=await J(`Updating your skills`,async()=>{if(!i){let t=await ad(e);return[t,t]}let t=await nd(e,a,i,{copy:r.copy});return[t,t.user]});for(let e of s.written.filter(e=>t.includes(e)))Y.dim(`Linked ${e} into ${nh}.`);for(let e of s.removed)Y.dim(`Unlinked ${e} from ${nh}.`);if(`project`in o){for(let e of o.project.removed)Y.dim(`Moved ${e} out of this project.`);for(let e of o.project.written.filter(e=>t.includes(e)))Y.dim(`${e} is still added to this project, so it is linked here again.`)}await ih(o,r)}async function ch(e,t,n,r,i){for(let e of n)r.includes(e)&&Y.dim(`${e} is global, so it is already in this project.`);let a=n.filter(e=>!r.includes(e));Vu(t,a);let o=Cu(i.cwd),s=await J(`Updating this project`,async()=>(await B(e),nd(e,o,t,{copy:i.copy})));for(let e of a)Y.step(`Added ${e} to this project.`);await ih(s,i)}async function lh(e,t,n,r){if(r.notGlobal)throw new L(`--not-global only applies to skills already in your library.`);let i=r.global?null:r.project??vl(r.cwd),a=await J(`Loading your library`,async()=>(await B(e),$u(e))),o=new Map(a.entries.map(e=>[e.name,e])),s=[...n,...t.skill?[t.skill]:[]],c=await Im(t,async e=>{let n=Vm(e,t.subpath);if(n.length===0)throw new L(`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 L(`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(!K)throw new L(`${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 pm(`Add from ${t.label}`,n.map(e=>({name:e.name,hint:uh(e.description)})));i=n.filter(t=>e.includes(t.name))}let a=[];for(let t of i)try{Yl(t.name),a.push({skill:Zl(t.dir,t.name),path:C.relative(e,t.dir)})}catch(e){Y.warn(`Skipped ${t.name}: ${e instanceof L?e.message:e}`)}return a},{interactive:K});if(c.length===0){Y.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?Xu(e.name,d.get(e.name)):l.push(e),u.push(e.name);continue}if(!(r.overwrite||K&&!r.yes&&await dm(`${e.name} is already in your library. Replace it with the one from ${t.label}?`))){Y.warn(`Kept your own ${e.name}. Pass --overwrite to replace it.`);continue}l.push(e),u.push(e.name)}let f=await J(`Saving ${l.length} skill(s) to your library`,()=>od(e,l,o,d));for(let e of f)Y.step(`Copied ${e} into your library from ${t.label}.`);if(u.length===0){e.report();return}r.global?await sh(e,u,!0,r):i?await ch(e,i,u,(await J(`Loading your library`,()=>$u(e))).entries.filter(e=>e.global).map(e=>e.name),r):(Y.blank(),Y.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 uh(e){if(e)return e.length>60?`${e.slice(0,57)}...`:e}function dh(e){switch(process.platform){case`darwin`:return[`open`,[e]];case`win32`:return[`rundll32`,[`url.dll,FileProtocolHandler`,e]];default:return[`xdg-open`,[e]]}}function fh(e){let[t,n]=dh(e);try{let e=r(t,n,{stdio:`ignore`,detached:!0,windowsHide:!0});e.on(`error`,()=>{}),e.unref()}catch{}}async function ph(){let e=c.randomBytes(16).toString(`hex`),t=Du(),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=f.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 L(`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}`;Y.info(`Opening your browser to sign in…`),Y.dim(r),Y.blank(),fh(r),Tu(await J(`Waiting for you to sign in`,()=>a))}finally{clearTimeout(s),o.close()}}const mh=Qm.extend({logout:I(),token:P().optional()}),hh=new x(`auth`).description(`Sign in to skilless.dev, or sign out.`).addOption(new S(`--logout`,`Sign out on this machine.`).default(!1)).addOption(new S(`--token <token>`,`Save a token directly, for when the browser flow cannot reach localhost.`)).addOption(Z.cwd).action(async e=>{let t=Q(mh,e);Y.intro(X),await $(async()=>{if(t.logout){if(!wu()){Y.info(`You were not signed in.`);return}Eu(),Y.step(`Signed out on this machine.`);return}if(t.token){Tu(t.token),Y.step(`Token saved. You are signed in to skilless.dev.`);return}await ph(),Y.step(`Signed in to skilless.dev.`)})}),gh=s.homedir(),_h=process.env.LOCALAPPDATA??C.join(gh,`AppData`,`Local`),vh=process.env.ProgramFiles??`C:/Program Files`,yh={cursor:{label:`Cursor`,cli:`cursor`,fallbacks:{darwin:[`/Applications/Cursor.app/Contents/Resources/app/bin/cursor`,`${gh}/Applications/Cursor.app/Contents/Resources/app/bin/cursor`],win32:[C.join(_h,`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`,`${gh}/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code`],win32:[C.join(_h,`Programs/Microsoft VS Code/bin/code.cmd`),C.join(vh,`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`,`${gh}/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf`],win32:[C.join(_h,`Programs/Windsurf/bin/windsurf.cmd`)],linux:[`/usr/share/windsurf/bin/windsurf`]}},zed:{label:`Zed`,cli:`zed`,fallbacks:{darwin:[`/Applications/Zed.app/Contents/MacOS/cli`,`${gh}/Applications/Zed.app/Contents/MacOS/cli`],win32:[C.join(_h,`Programs/Zed/bin/zed.exe`)],linux:[`${gh}/.local/bin/zed`]}},sublime:{label:`Sublime Text`,cli:`subl`,fallbacks:{darwin:[`/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl`],win32:[C.join(vh,`Sublime Text/subl.exe`)],linux:[`/opt/sublime_text/sublime_text`]}},neovim:{label:`Neovim`,cli:`nvim`,terminal:!0},vim:{label:`Vim`,cli:`vim`,terminal:!0}},bh=Object.keys(yh);function xh(e){return Object.hasOwn(yh,e)}function Sh(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 Ch(e){let t=yh[e],n=Sh(t.cli);if(n)return n;let r=process.platform;return t.fallbacks?.[r]?.find(e=>R(e))??null}function wh(){return bh.filter(e=>Ch(e)!==null)}function Th(e){return yh[e].terminal===!0}function Eh(e,t){let n=Ch(e);if(!n)return Promise.reject(Error(`${yh[e].label} was not found. Is \`${yh[e].cli}\` on your PATH?`));let i=Th(e);return new Promise((e,a)=>{let o=r(n,[t],{cwd:C.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 Dh(){let e=dl(Nl,{});return e.editor!==void 0&&!xh(e.editor)&&delete e.editor,e}function Oh(e){fl(Nl,e)}function kh(e){let t={...Dh(),...e};for(let e of Object.keys(t))t[e]===void 0&&delete t[e];return Oh(t),t}const Ah=`none`,jh=Cc({unset:I()}),Mh=new x(`editor`).description("Choose the editor `skilless create` opens a new skill in.").argument(`[editor]`,`Set it directly: ${bh.join(`, `)}.`).addOption(new S(`--unset`,`Stop opening new skills in an editor.`).default(!1)).action(async(e,t)=>{let n=Q(jh,t);Y.intro(X),await $(async()=>{let t=Dh().editor;if(n.unset){kh({editor:void 0}),Y.step(`New skills will no longer open in an editor.`);return}if(e){if(!xh(e))throw new L(`${e} is not an editor skilless knows.`,{suggestion:`Pick one of: ${bh.join(`, `)}.`});kh({editor:e}),Y.step(`New skills will open in ${yh[e].label}.`);return}if(!K){Y.info(t?`Editor: ${yh[t].label}`:`No editor set.`);return}let r=wh();if(r.length===0)throw new L(`None of the editors skilless knows are installed.`,{suggestion:`skilless looks for: ${bh.map(e=>yh[e].cli).join(`, `)}.`});let i=await mm(`Open new skills in`,[...r.map(e=>({name:e,message:yh[e].label,hint:e===t?G.default.gray(`current`):void 0})),{name:Ah,message:`None`,hint:t?void 0:G.default.gray(`current`)}],t??Ah);if(i===Ah){kh({editor:void 0}),Y.step(`New skills will not open in an editor.`);return}xh(i)&&(kh({editor:i}),Y.step(`New skills will open in ${yh[i].label}.`))})}),Nh=new x(`config`).description(`View or change your skilless settings.`).addCommand(Mh).action(async()=>{Y.intro(X),await $(async()=>{let e=Dh();Y.info(`editor ${e.editor?yh[e.editor].label:G.default.gray(`not set`)}`),Y.blank(),Y.dim(`Stored in ${Nl}`)})}),Ph=Qm.extend({description:P().optional(),project:P().optional(),global:I().optional(),open:I()}),Fh=new x(`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(Z.project).addOption(Z.cwd).action(async(e,t)=>{let n=Q(Ph,t);Y.intro(X),await $(async()=>{let t=new Am,r=await J(`Loading your library`,async()=>(await B(t),$u(t))),i=new Set(r.entries.map(e=>e.name)),a=e;if(!a){if(!K)throw new L(`No name given.`,{suggestion:"Run `skilless create <name>`."});a=await fm(`Name`,{validate:e=>e?Jl(e)?i.has(e)?`${e} already exists in your library.`:!0:`Use lowercase letters, digits, dots, dashes and underscores.`:`A name is required.`})}if(Yl(a),tu(a))throw new L(`${a} already exists in your library.`,{suggestion:`Edit it at ${C.join(Fl(a),`SKILL.md`)}`});if(i.has(a))throw new L(`${a} already exists in your library.`,{suggestion:`Run \`skilless sync\` to bring ${a} down to this machine.`});let o=n.description??await fm(`What does this skill do?`);Ql(a,nu(a,o));let s=Zl(Fl(a),a);if(await J(`Creating ${a}`,()=>t.try(e=>e.putSkill(a,s.files,s.editedAt)))){let e=ku();e.skills[a]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:Date.now()},Au(e)}Y.step(`Created ${a} in your library.`),n.global&&Uu([a],!0);let c=n.project??vl(n.cwd);if(c){n.global||Vu(c,[a]);let e=Cu(n.cwd);await ih(await J(`Updating this project`,async()=>(await B(t),nd(t,e,c))))}else n.global?await ih(await J(`Linking your global skills`,async()=>(await B(t),ad(t)))):await J(`Saving to skilless.dev`,()=>B(t));n.global?Y.step(`${a} is now global, so every project has it.`):c?Y.step(`Added ${a} to ${c}.`):Y.warn(`No git remote here, so ${a} was not added to a project.`);let l=C.join(Fl(a),`SKILL.md`),u=n.open?Dh().editor:void 0,d=u&&(!Th(u)||K);if(Y.blank(),u&&d)try{Y.dim(`Opening ${a} in ${yh[u].label}...`),await Eh(u,l)}catch(e){Y.warn(e instanceof Error?e.message:String(e)),Y.dim(`Edit ${a} at ${l}`)}else Y.dim(`Edit ${a} at ${l}`);t.api&&Y.dim("Run `skilless sync` to save your changes."),t.report()})}),Ih=Qm.extend({yes:I()}),Lh=new x(`delete`).description(`Delete skills from your library, everywhere.`).argument(`<skills...>`,`Skills to delete.`).addOption(Z.yes).addOption(Z.cwd).action(async(e,t)=>{let n=Q(Ih,t);Y.intro(X),await $(async()=>{let t=new Am,r=await J(`Loading your library`,async()=>(await B(t),$u(t))),i=new Set(r.entries.map(e=>e.name)),a=e.filter(e=>!i.has(e));if(a.length>0)throw new L(`Not in your library: ${a.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});if(Y.warn(`This deletes ${e.join(`, `)} from every project, on every machine.`),Y.dim(`Run \`skilless remove ${e.join(` `)}\` to take ${e.length===1?`it`:`them`} out of just this project.`),!(n.yes||await dm(`Delete ${e.join(`, `)}?`,!1))){Y.info(`Nothing was deleted.`);return}Gu(e),Zu(e);let o=ku(),s=Cu(n.cwd);for(let t of e)z(Fl(t)),delete o.skills[t],Y.step(`Deleted ${t} from your library.`);Au(o),xu(s,e),xu(iu(),e),await J(`Deleting from skilless.dev`,()=>B(t)),Y.blank(),Y.dim("Other checkouts will clean up on their next `skilless install`."),Y.dim(`Restore at skilless.dev/skills within 30 days.`),t.report()})}),Rh=Qm.extend({yes:I(),overwrite:I()});function zh(e){return a.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(t=>({name:t.name,dir:C.join(e,t.name)})).filter(e=>R(C.join(e.dir,Wl))).sort((e,t)=>e.name.localeCompare(t.name))}const Bh=new x(`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(Z.yes).addOption(Z.cwd).action(async(e,t)=>{let n=Q(Rh,t);Y.intro(X),await $(async()=>{let t=new Am,r=C.resolve(n.cwd,e);if(!R(r))throw new L(`${r} does not exist.`);let i=zh(r);if(i.length===0)throw new L(`No skills found in ${r}.`,{suggestion:`A skill is a directory containing a ${Wl}.`});let a=await J(`Loading your library`,async()=>(await B(t),$u(t))),o=new Map(a.entries.map(e=>[e.name,e])),s=[],c=[];for(let e of i){if(!Jl(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(Zl(e.dir,e.name))}catch(t){c.push({name:e.name,reason:t instanceof Error?t.message:`unreadable`})}}for(let e of c)Y.warn(`Skipped ${e.name}: ${e.reason}.`);if(s.length===0){Y.info(`Nothing to import.`),t.report();return}if(Y.blank(),Y.info(`Importing: ${s.map(e=>e.name).join(`, `)}`),!(n.yes||await dm(`Import ${s.length} skill(s)?`,!0)))return;let l=await J(`Importing ${s.length} skill(s)`,()=>od(t,s,o));for(let e of l)Y.step(`Imported ${e} into your library.`);Y.blank(),Y.dim(`Run \`skilless add ${s[0]?.name??`<skill>`}\` to add one to this project.`),t.report()})}),Vh=new x(`init`).description(`Create the local skilless directory.`).addOption(Z.cwd).action(async e=>{Q(Qm,e),Y.intro(X),await $(async()=>{let e=!R(Dl);rl(Dl),Y.step(`${e?`Created`:`Found`} your library at ${El}.`),Y.blank(),Y.dim("Run `skilless create <name>` to make a skill."),Y.dim("Run `skilless import <dir>` to bring in skills you already have."),wu()||Y.dim("Run `skilless auth` to sync your skills with the cloud.")})}),Hh=Qm.extend({project:P().optional(),copy:I().optional()}),Uh=new x(`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(Z.project).addOption(Z.cwd).action(async e=>{let t=Q(Hh,e);Y.intro(X),await $(async()=>{let e=eh(),n=th(t.cwd,t.project),r=Cu(t.cwd),i=await J(`Installing ${n}'s skills`,async()=>(await Ku(e),Pu(e,r,n,{copy:t.copy}))),{project:a,user:o}=i;for(let e of[...a.pruned,...o.pruned])Y.dim(`Pruned ${e}, which is no longer in your library.`);for(let e of[...a.fetched,...o.fetched])Y.dim(`Fetched ${e} from your library.`);for(let e of a.removed)Y.dim(`Removed ${e}, which is no longer in this project.`);for(let e of o.removed)Y.dim(`Removed ${e} from ${nh}, since it is no longer global.`);for(let e of a.written)Y.step(`Installed ${e}.`);for(let e of o.written)Y.step(`Installed ${e} globally.`);if(i.count===0){Y.info(`No skills added to ${n} yet.`),Y.dim("Run `skilless add <skill>` to add one.");return}await ih(i,{copy:t.copy}),Y.blank(),Y.dim(ju(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.`)})}),Wh=Qm.extend({project:Tc([I(),P()]).optional()});function Gh(e,t){let n=[];return e.global&&n.push(`global`),e.source&&n.push(`from ${_l(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 Kh=new x(`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(Z.cwd).action(async e=>{let t=Q(Wh,e);Y.intro(X),await $(async()=>{let e=new Am,n=await J(`Loading your library`,async()=>(await B(e),$u(e)));if(t.project!==void 0){let r=th(t.cwd,typeof t.project==`string`?t.project:void 0),i=td(n,await J(`Loading ${r}`,()=>ed(e,r)));Y.info(`${G.default.bold(r)}`),Y.blank(),i.length===0&&Y.dim("No skills in this project. Run `skilless add <skill>` to add one.");for(let t of i)Y.dim(` ${t.name}${G.default.gray(Gh(t,e.api!==null))}`);e.report();return}n.entries.length===0&&Y.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);Y.dim(` ${t.name.padEnd(28)} ${G.default.gray(r+Gh(t,e.api!==null))}`)}e.report()})});function qh(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 Jh(e,t,n){return Yh(e,t===`push`?`remote`:`local`,n)}function Yh(e,t,n){let r=new Date().toISOString().replace(/[:.]/g,`-`),i=C.join(Pl,e,`${r}-${t}`);for(let e of n)ul(C.join(i,e.path),e.contents);return i}async function Xh(e,t,n){let r=Array(e.length),i=0;return await Promise.all(Array.from({length:Math.min(t,e.length)},async()=>{for(;i<e.length;){let t=i++;r[t]=await n(e[t])}})),r}async function Zh(e,t,n){let r={actions:[],conflicts:[],skipped:[]},i=Date.now(),a=new Set;for(let e of t)e.type!==`delete-local`&&e.type!==`delete-remote`||(n.yes||await dm(e.type===`delete-local`?`Delete ${e.name} from this machine? It was deleted elsewhere.`:`Delete ${e.name} everywhere? It was deleted on this machine.`,!0)?a.add(e):r.skipped.push(e.name));let o=await Xh(t,8,async t=>{let{name:o}=t,s=n.local.get(o);switch(t.type){case`noop`:return s&&(n.state.skills[o]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:i}),!0;case`push`:case`conflict-push`:if(!s)return!0;if(t.type===`conflict-push`){let t=await e.getSkill(o);t&&r.conflicts.push({name:o,kept:`push`,stashedAt:Jh(o,`push`,t.files)})}return n.remote.set(o,await e.putSkill(o,s.files,s.editedAt)),n.state.skills[o]={contentHash:s.contentHash,editedAt:s.editedAt,syncedAt:i},!0;case`pull`:case`conflict-pull`:{let a=await e.getSkill(o);return a?(t.type===`conflict-pull`&&s&&r.conflicts.push({name:o,kept:`pull`,stashedAt:Jh(o,`pull`,s.files)}),Ql(o,a.files,a.editedAt),n.state.skills[o]={contentHash:a.contentHash,editedAt:a.editedAt,syncedAt:i},!0):!0}case`delete-local`:return a.has(t)?(z(Fl(o)),delete n.state.skills[o],!0):!1;case`delete-remote`:return a.has(t)?(await e.deleteSkill(o),n.remote.delete(o),delete n.state.skills[o],!0):!1}});return r.actions=t.filter((e,t)=>o[t]),r.conflicts.sort((e,t)=>e.name.localeCompare(t.name)),r}function Qh(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 $h=Qm.extend({yes:I(),user:I().optional(),includeCommitted:I().optional()});function eg(e){let t=Math.max(0,Math.round((Date.now()-e)/6e4));if(t<1)return`just now`;if(t<60)return`${t} min ago`;let n=Math.round(t/60);if(n<24)return`${n} hour${n===1?``:`s`} ago`;let r=Math.round(n/24);return`${r} day${r===1?``:`s`} ago`}function tg(e,t){return R(e)?a.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.isSymbolicLink()).map(n=>({name:n.name,dir:C.join(e,n.name),scope:t})).filter(e=>R(C.join(e.dir,Wl))):[]}async function ng(e,t){if(t.includeCommitted)return`move`;if(t.yes||!K)return Y.warn(`Skipped ${e}: it is committed to this repo. Pass --include-committed to move it out of git.`),`leave`;Y.warn(`${e} is committed to this repo.`),Y.note(`Moving it untracks it from git, so teammates lose it on their next pull unless they use skilless.`);let n=await mm(`What should happen to ${e}?`,[{name:`leave`,message:`Leave it`},{name:`copy`,message:`Copy it to my library`,hint:`keep it committed`},{name:`move`,message:`Move it`,hint:`untrack it from git`}]);return n===`copy`||n===`move`?n:`leave`}const rg=new x(`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.`).argument(`[skills...]`,`Skills to migrate, by name. Omit to migrate everything found.`).option(`--user`,`Migrate your user-level skills too, without asking.`).option(`--include-committed`,`Move skills committed to this repo too, without asking. Their removal from git is staged, not committed.`).addOption(Z.yes).addOption(Z.cwd).action(async(e,t)=>{let n=Q($h,t);Y.intro(X),await $(async()=>{let t=new Am,r=Cu(n.cwd),i=vl(n.cwd),a=t=>e.length===0||e.includes(t.name),o=(i?[Il,Ll].flatMap(e=>tg(C.join(r,e),`project`)):[]).filter(a),s=[...tg(Bl(),`user`),...tg(zl(),`user`)].filter(a),c=e.filter(e=>![...o,...s].some(t=>t.name===e));if(c.length>0)throw new L(`Nothing to migrate named ${c.join(`, `)}.`,{suggestion:i?`Migrate looks for real skill directories in ${Il}, ${Ll} and at the user level. Linked ones are already in skilless.`:`This directory has no git remote, so only user-level skills can be migrated from it.`});let l=e=>xl(r,C.relative(r,e.dir)),u=[...new Set(o.filter(l).map(e=>e.name))].sort(),d=new Map;for(let e of u)d.set(e,await ng(e,n));let f=[],p=[];for(let e of o){let t=d.get(e.name);t?t===`copy`?p.push({...e,committed:`copy`}):t===`move`&&p.push(l(e)?{...e,committed:`move`}:e):f.push(e)}if(i||Y.dim(`No git remote here, so there are no project skills to migrate.`),f.length===0&&p.length===0&&s.length===0){Y.info(`Nothing to migrate.`);return}let m=await J(`Loading your library`,async()=>(await B(t),$u(t))),h=new Map(m.entries.map(e=>[e.name,e])),g=new Map,_=async(e,r)=>{let{name:i}=e,a=h.get(i),o=g.get(i);if(!o){if(n.yes||!K)Y.warn(`Skipped ${i}: a different ${i} is already in your library. Run migrate in a terminal to pick one.`),o=`skip`;else{let t=a.local?.editedAt??a.remote?.editedAt;Y.warn(`A different ${i} is already in your library.`),Y.note(`On disk: edited ${eg(e.editedAt)}, in ${r.map(e=>rh(e.dir)).join(` and `)}`),Y.note(`In your library: edited ${t?eg(t):`at an unknown time`}`);let n=await mm(`Which ${i} do you want to keep?`,[{name:`library`,message:`The one in my library`,hint:`replaces the copy on disk`},{name:`local`,message:`The one on disk`,hint:`replaces it in your library, everywhere`},{name:`skip`,message:`Skip it for now`}]);o=n===`library`||n===`local`?n:`skip`}g.set(i,o)}if(o===`skip`)return null;let s=a.local?.files??(await J(`Fetching ${i} from your library`,()=>t.try(e=>e.getSkill(i))))?.files;if(!s)return Y.warn(`Skipped ${i}: could not fetch the one in your library.`),null;if(o===`library`){let t=a.local?.editedAt??a.remote.editedAt;return{skill:{...e,files:s,editedAt:t,contentHash:a.local?.contentHash??a.remote.contentHash},adopted:!0,stash:{label:`local`,files:e.files}}}return{skill:{...e,editedAt:Date.now()},adopted:!1,stash:{label:`library`,files:s}}},v=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(!Jl(e)){Y.warn(`Skipped ${e}: not a valid skill name.`);continue}let t;try{t=r.map(t=>Zl(t.dir,e))}catch(t){Y.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)){Y.warn(`Skipped ${e}: found more than one version (${r.map(e=>e.dir).join(`, `)}).`);continue}let a=h.get(e),o=a?.local?.contentHash??a?.remote?.contentHash,s=o&&o!==i.contentHash?await _(i,r):{skill:i,adopted:o!==void 0};s&&n.push({...s,dirs:r.filter(e=>e.committed!==`copy`).map(e=>e.dir),untrack:r.filter(e=>e.committed===`move`).map(e=>e.dir),copyOnly:r.every(e=>e.committed===`copy`),scope:r.some(e=>e.scope===`user`)?`user`:`project`})}return n},y=await v(f);if(y.length>0&&(Y.list(`Project skills in ${i}`,y.map(e=>e.skill.name)),n.yes||e.length>0||await dm(y.length===1?`Move it into skilless and link it here?`:`Move these ${y.length} into skilless and link them here?`,!0)||(y=[])),y.push(...await v(p)),s.length>0){let t=[...new Set(s.map(e=>e.name))].sort();Y.list(`User-level skills`,t);let r=n.user===!0||e.length>0;if(!r&&!n.yes&&K&&(Y.note(`They would become global skills, linked back into ~/.agents/skills so every project still gets them.`),r=await dm(t.length===1?`Migrate it too?`:`Migrate these ${t.length} too?`,!1)),r){let e=new Set(y.map(e=>e.skill.name)),t=await v([...s,...[...f,...p].filter(e=>s.some(t=>t.name===e.name))]);y=[...y.filter(e=>!t.some(t=>t.skill.name===e.skill.name)),...t];for(let t of e)y.some(e=>e.skill.name===t)||Y.warn(`Skipped ${t}: its project and user-level copies differ.`)}}if(y.length===0){Y.info(`Nothing was migrated.`);return}let ee=ku(),te=[];for(let{skill:e,adopted:n,stash:r}of y)r&&te.push(Yh(e.name,r.label,r.files)),Ql(e.name,e.files,e.editedAt),!n&&(await J(`Saving ${e.name} to your library`,()=>t.try(t=>t.putSkill(e.name,e.files,e.editedAt)))?ee.skills[e.name]={contentHash:e.contentHash,editedAt:e.editedAt,syncedAt:Date.now()}:delete ee.skills[e.name]);Au(ee);let b=[];for(let{dirs:e,untrack:t}of y)for(let n of e){let e=C.relative(r,n);if(t.includes(n)){if(!Sl(r,e)){Y.warn(`Could not untrack ${e} from git, so it was left in place.`);continue}b.push(e)}z(n)}let ne=y.filter(e=>e.scope===`user`).map(e=>e.skill.name),re=y.filter(e=>e.scope===`project`&&!e.copyOnly).map(e=>e.skill.name);ne.length>0&&Uu(ne,!0),i&&re.length>0&&Vu(i,re),await J(`Saving to skilless.dev`,()=>B(t));let ie=e=>y.filter(e).map(e=>e.skill.name),ae=[[`Moved into your library and this project`,ie(e=>re.includes(e.skill.name)&&!e.adopted)],[`Already in your library, now linked to this project`,ie(e=>re.includes(e.skill.name)&&e.adopted)],[`Moved into your library as global skills`,ie(e=>e.scope===`user`&&!e.adopted)],[`Already in your library, now linked as global skills`,ie(e=>e.scope===`user`&&e.adopted)],[`Copied into your library, still committed here`,ie(e=>e.copyOnly&&!e.adopted)],[`Already in your library, still committed here`,ie(e=>e.copyOnly&&e.adopted)]];for(let[e,t]of ae)t.length>0&&Y.list(e,t,!0);if(te.length>0){Y.note(`The copies you did not keep are saved here:`);for(let e of te)Y.dim(` ${rh(e)}`)}b.length>0&&Y.note(`Staged the removal of ${b.join(`, `)} from git. Commit it when you are ready.`),i?await ih(await J(`Updating this project`,()=>nd(t,r,i)),{yes:n.yes}):ne.length>0&&await ih(await J(`Linking your global skills`,()=>ad(t)),{yes:n.yes}),t.report()})}),ig=Qm.extend({project:P().optional(),copy:I().optional()}),ag=new x(`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(Z.project).addOption(Z.cwd).action(async(e,t)=>{let n=Q(ig,t);Y.intro(X),await $(async()=>{let t=new Am,r=th(n.cwd,n.project),[i,a]=await J(`Loading your library`,async()=>(await B(t),[await $u(t),new Set(await ed(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)){Y.warn(`${t} is global, so it is in every project.`),Y.dim(` Run \`skilless add --not-global ${t}\` to take it out of every project.`);continue}if(!a.has(t)){Y.warn(`${t} is not in this project.`);continue}s.push(t)}if(s.length===0){t.report();return}Hu(r,s);let c=Cu(n.cwd),l=await J(`Updating this project`,async()=>(await B(t),nd(t,c,r,{copy:n.copy})));for(let e of s)Y.step(`Removed ${e} from this project.`);await ih(l,{copy:n.copy}),Y.blank(),Y.dim(`Run \`skilless delete ${s.join(` `)}\` to delete ${s.length===1?`it`:`them`}.`),t.report()})}),og=Qm.extend({yes:I(),push:I().optional(),pull:I().optional()}),sg=new x(`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(Z.yes).addOption(Z.cwd).action(async e=>{let t=Q(og,e);Y.intro(X),await $(async()=>{let e=eh(),[n,r]=await J(`Checking skilless.dev`,async()=>{let t=await e.listSkills(),n=await Ku(e,t);return[n,n>0?await e.listSkills():t]}),i=new Map(eu().map(e=>[e.name,e])),a=new Map(r.map(e=>[e.name,e])),o=ku(),s=qh(i,a,o,t.push?`push`:t.pull?`pull`:void 0),c=await J(`Syncing your library`,()=>Zh(e,s,{local:i,remote:a,state:o,yes:t.yes}));Au(o);let l=c.actions.map(Qh).filter(e=>e!==null);for(let e of l)Y.step(e);let[u,d]=await J(`Syncing your library`,async()=>{let t=[...a.values()],r=await Ku(e,t);return[n+r,r>0?await e.listSkills():t]});u>0&&Y.step(`Sent ${u} change(s) made while offline.`),Hl(d),Qu(d);let f=vl(t.cwd),p=await J(f?`Updating this project`:`Updating your global skills`,async()=>{if(!f)return{user:Iu(d.filter(e=>e.global).map(e=>e.name))};let n=await Pu(e,Cu(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)Y.step(`Linked ${e} into this project.`);for(let e of m.removed)Y.step(`Unlinked ${e}, which is no longer in this project.`);for(let e of m.pruned)Y.step(`Unlinked ${e}, which is no longer in your library.`)}for(let e of h.added)Y.step(`Linked ${e} into ${nh}.`);for(let e of h.removed)Y.step(`Unlinked ${e} from ${nh}, since it is no longer global.`);for(let e of h.pruned)Y.step(`Unlinked ${e} from ${nh}, 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&&Y.info(`Everything is already in sync.`),c.conflicts.length>0){Y.blank(),Y.warn(`${c.conflicts.length} skill(s) changed in two places. The copy that lost is kept here:`);for(let e of c.conflicts)Y.dim(` ${e.stashedAt}`)}await ih({skipped:[...m?.skipped??[],...h.skipped]},{yes:t.yes}),c.skipped.length>0&&(Y.blank(),Y.dim(`Skipped ${c.skipped.join(`, `)}.`))})}),cg=Qm.extend({yes:I(),force:I()});function lg(e){let t=_l(e.url)??e.url;return e.ref?`${t}#${e.ref}`:t}const ug=new x(`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(Z.yes).addOption(Z.cwd).action(async(e,t)=>{let n=Q(cg,t);Y.intro(X),await $(async()=>{let t=new Am,r=await J(`Loading your library`,async()=>(await B(t),$u(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 L(`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 L(`${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){Y.info(`None of your skills were added from a repository.`),Y.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=lg(r);await Im({url:r.url,ref:r.ref,label:i},async r=>{for(let a of e){let e=Hm(r,a.source.path,a.name);if(!e){Y.warn(`${a.name} is no longer in ${i}.`);continue}let o;try{o=Zl(e.dir,a.name)}catch(e){Y.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){Xu(a.name,s),u.push(a.name);continue}if(p!==a.source.hash){let e=n.force===!0;if(!e&&K&&!n.yes&&(Y.note(`You have edited ${a.name} since it came from ${i}.`),e=await dm(`Replace your edits to ${a.name}?`)),!e){d.push(a.name);continue}let r=a.local?.files??(await J(`Fetching your copy of ${a.name}`,()=>t.try(e=>e.getSkill(a.name))))?.files;if(!r){Y.warn(`Couldn't fetch your copy of ${a.name} to keep it, so it was left alone.`);continue}f.push({name:a.name,dir:Yh(a.name,`update`,r)})}c.push(o),l.set(a.name,s)}},{interactive:K}).then(()=>!0,e=>{if(!(e instanceof L))throw e;return Y.warn(e.toString()),!1})||p.push(...e.map(e=>e.name))}Y.blank();let m=await J(`Saving ${c.length} skill(s) to your library`,()=>od(t,c,i,l));for(let e of m)Y.step(`Updated ${e}.`);if(u.length>0&&Y.info(u.length===1?`${u[0]} is already up to date.`:`${u.length} skills are already up to date.`),d.length>0&&Y.warn(`Kept your edits to ${d.join(`, `)}. Run \`skilless update ${d.join(` `)} --force\` to replace them.`),p.length>0&&Y.warn(`Couldn't check ${p.join(`, `)}.`),f.length>0){Y.blank(),Y.dim(`Your edited copies are kept here:`);for(let e of f)Y.dim(` ${e.dir}`)}t.report()})}),dg=Qm.extend({project:P().optional(),yes:I()}),fg=new x(`vendor`).description(`Copy skills into this repo as real files to commit, instead of linking them.`).argument(`[skills...]`,`Skills to vendor. Omit to pick from a list.`).addOption(Z.yes).addOption(Z.project).addOption(Z.cwd).action(async(e,t)=>{let n=Q(dg,t);Y.intro(X),await $(async()=>{let t=hl(n.cwd);if(!t)throw new L(`This directory is not in a git repo.`,{suggestion:`Vendored skills are meant to be committed, so run this inside one.`});let r=new Am,i=n.project??vl(n.cwd),[a,o]=await J(`Loading your library`,async()=>(await B(r),[await $u(r),new Set(i?await ed(r,i):[])])),s=new Map(a.entries.map(e=>[e.name,e])),c=e.filter(e=>!s.has(e));if(c.length>0)throw new L(`Not in your library: ${c.join(`, `)}`,{suggestion:"Run `skilless list` to see what you have."});let l=e;if(l.length===0){if(a.entries.length===0){Y.info(`Your library is empty.`),r.report();return}if(l=await pm(`Vendor into this repo`,a.entries.map(e=>({name:e.name,...o.has(e.name)?{hint:`in this project`}:{}}))),l.length===0){Y.info(`Nothing selected.`);return}}let u=[];for(let e of l){let i=await J(`Reading ${e}`,()=>pg(r,s.get(e)));if(!i){Y.warn(`${e} is not on this machine, and the server can't be reached to fetch it.`);continue}let a=bu(t,e);if(a.length>0){let t=a.join(` and `);if(!(n.yes||K&&await dm(`Overwrite ${t}?`,!1))){Y.warn(`Skipped ${e}: ${t} was not made by skilless.`),K||Y.dim(` Run this in a terminal to overwrite it, or pass --yes.`);continue}}yu(t,{name:e,files:i}),u.push(e),Y.step(`Vendored ${e} into ${Il}/${e}.`)}if(u.length===0){r.report();return}let d=i?u.filter(e=>o.has(e)):[];if(i&&d.length>0){Hu(i,d),await J(`Updating this project`,()=>B(r));for(let e of d)Y.dim(`Took ${e} out of this project in skilless.`)}for(let e of u.filter(e=>s.get(e).global))Y.dim(`${e} is global, so it is also still linked at the user level.`);Y.blank(),Y.dim(`Commit ${u.flatMap(e=>[`${Il}/${e}`,`${Ll}/${e}`]).join(`, `)} to share ${u.length===1?`it`:`them`}.`),r.report()})});async function pg(e,t){return t.local?t.local.files:(await e.try(e=>e.getSkill(t.name)))?.files??null}ce.name(Ym).description(Xm).version(X).commandsGroup(`Commands:`).addCommand(Vh).addCommand(Fh).addCommand(oh).addCommand(ug).addCommand(ag).addCommand(Lh).addCommand(Uh).addCommand(fg).addCommand(Bh).addCommand(rg).addCommand(Kh).addCommand(Nh).commandsGroup(`Cloud Commands (skilless.dev):`).addCommand(hh).addCommand(sg).commandsGroup(`Commands:`).helpCommand(!0).parse();export{};
|