From fcd77ef70e851de84cb5ecece311a2f4570ff670 Mon Sep 17 00:00:00 2001 From: Michael Leydon Date: Sat, 28 Mar 2026 22:31:46 -0400 Subject: [PATCH] feat: add project-level skill scanning and Projects sidebar filter Scan all directories under a configurable home folder for project-level skills (.claude/skills, .codex/skills, .cursor/skills, etc.) and display them grouped by project in a new "Projects" sidebar section. - Add projectScanEnabled and projectsHomeDir settings - Scan ~/project/.tool/skills for each installed tool using the tool's own path config (correct tool attribution) - Add Projects section to sidebar with per-project filtering - Show absolute file path in frontmatter detail view - Skip .Trash, Library, and other system dirs to avoid EPERM crashes - Try/catch per-project scan so one failure doesn't kill the rest Co-Authored-By: Claude Opus 4.6 (1M context) --- main.js | 2 +- src/main.ts | 2 +- src/scanner.ts | 87 ++++++++++++++++++++++++++++++++++++++++++-- src/settings.ts | 35 ++++++++++++++++++ src/store.ts | 18 ++++++++- src/types.ts | 7 +++- src/views/detail.ts | 10 ++++- src/views/sidebar.ts | 20 ++++++++++ 8 files changed, 172 insertions(+), 9 deletions(-) diff --git a/main.js b/main.js index dd30db1..9a0d736 100644 --- a/main.js +++ b/main.js @@ -1 +1 @@ -var R=Object.defineProperty;var bt=Object.getOwnPropertyDescriptor;var kt=Object.getOwnPropertyNames;var St=Object.prototype.hasOwnProperty;var yt=(a,t)=>{for(var e in t)R(a,e,{get:t[e],enumerable:!0})},wt=(a,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of kt(t))!St.call(a,i)&&i!==e&&R(a,i,{get:()=>t[i],enumerable:!(s=bt(t,i))||s.enumerable});return a};var Dt=a=>wt(R({},"__esModule",{value:!0}),a);var Vt={};yt(Vt,{default:()=>U});module.exports=Dt(Vt);var ft=require("obsidian");var rt=require("obsidian");var E=require("obsidian"),K=require("electron");var Z=require("os"),m=require("fs"),c=require("path"),h=(0,Z.homedir)(),C=process.env.XDG_CONFIG_HOME||(0,c.join)(h,".config");function P(a){return(0,m.existsSync)(`/Applications/${a}.app`)||(0,m.existsSync)((0,c.join)(h,"Applications",`${a}.app`))}function w(a){let t=[`/usr/local/bin/${a}`,`/opt/homebrew/bin/${a}`,(0,c.join)(h,".local","bin",a)];for(let s of t)if((0,m.existsSync)(s))return!0;let e=(0,c.join)(h,".nvm","versions","node");try{let{readdirSync:s}=require("fs");for(let i of s(e))if((0,m.existsSync)((0,c.join)(e,i,"bin",a)))return!0}catch{}return!1}var v=[{id:"claude-code",name:"Claude Code",color:"#f97316",icon:"brain",paths:[{baseDir:(0,c.join)(h,".claude","skills"),type:"skill",pattern:"directory-with-skillmd"},{baseDir:(0,c.join)(h,".claude","commands"),type:"command",pattern:"flat-md"}],agentPaths:[{baseDir:(0,c.join)(h,".claude","agents"),type:"agent",pattern:"flat-md"}],isInstalled:()=>(0,m.existsSync)((0,c.join)(h,".claude","settings.json"))||(0,m.existsSync)((0,c.join)(h,".claude","CLAUDE.md"))||w("claude")},{id:"cursor",name:"Cursor",color:"#3b82f6",icon:"mouse-pointer",paths:[{baseDir:(0,c.join)(h,".cursor","skills"),type:"skill",pattern:"directory-with-skillmd"},{baseDir:(0,c.join)(h,".cursor","rules"),type:"rule",pattern:"flat-md"}],agentPaths:[{baseDir:(0,c.join)(h,".cursor","agents"),type:"agent",pattern:"flat-md"}],isInstalled:()=>P("Cursor")||(0,m.existsSync)((0,c.join)(h,".cursor","argv.json"))},{id:"windsurf",name:"Windsurf",color:"#14b8a6",icon:"wind",paths:[{baseDir:(0,c.join)(h,".codeium","windsurf","memories"),type:"memory",pattern:"flat-md"},{baseDir:(0,c.join)(h,".windsurf","rules"),type:"rule",pattern:"flat-md"}],agentPaths:[],isInstalled:()=>P("Windsurf")||(0,m.existsSync)((0,c.join)(h,".codeium","windsurf","argv.json"))},{id:"codex",name:"Codex",color:"#22c55e",icon:"book",paths:[{baseDir:(0,c.join)(h,".codex","skills"),type:"skill",pattern:"directory-with-skillmd"},{baseDir:(0,c.join)(h,".codex","prompts"),type:"command",pattern:"flat-md"},{baseDir:(0,c.join)(h,".codex","memories"),type:"memory",pattern:"flat-md"}],agentPaths:[{baseDir:(0,c.join)(h,".codex","agents"),type:"agent",pattern:"flat-md"}],isInstalled:()=>(0,m.existsSync)((0,c.join)(h,".codex","config.toml"))||(0,m.existsSync)((0,c.join)(h,".codex","auth.json"))||w("codex")},{id:"copilot",name:"Copilot",color:"#a855f7",icon:"plane",paths:[{baseDir:(0,c.join)(h,".copilot","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>(0,m.existsSync)((0,c.join)(h,".copilot"))||w("copilot")},{id:"amp",name:"Amp",color:"#ec4899",icon:"zap",paths:[{baseDir:(0,c.join)(C,"amp","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>(0,m.existsSync)((0,c.join)(C,"amp","config.json"))||(0,m.existsSync)((0,c.join)(C,"amp","settings.json"))||w("amp")},{id:"opencode",name:"OpenCode",color:"#ef4444",icon:"terminal",paths:[{baseDir:(0,c.join)(C,"opencode","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>P("OpenCode")||(0,m.existsSync)((0,c.join)(C,"opencode","opencode.json"))||(0,m.existsSync)((0,c.join)(C,"opencode","opencode.jsonc"))||w("opencode")},{id:"pi",name:"Pi",color:"#06b6d4",icon:"sparkles",paths:[{baseDir:(0,c.join)(h,".pi","agent","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>w("pi")},{id:"antigravity",name:"Antigravity",color:"#ef4444",icon:"arrow-up-circle",paths:[{baseDir:(0,c.join)(h,".gemini","antigravity","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>P("Antigravity")||(0,m.existsSync)((0,c.join)(h,".gemini","antigravity","skills"))||w("antigravity")},{id:"claude-desktop",name:"Claude Desktop",color:"#f97316",icon:"monitor",paths:[],agentPaths:[],isInstalled:()=>P("Claude")},{id:"global-agents",name:"Global",color:"#a3e635",icon:"globe",paths:[{baseDir:(0,c.join)(h,".agents","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>(0,m.existsSync)((0,c.join)(h,".agents","skills"))},{id:"aider",name:"Aider",color:"#eab308",icon:"wrench",paths:[],agentPaths:[],isInstalled:()=>w("aider")}];var x={"claude-code":{viewBox:"0 0 24 24",paths:''},cursor:{viewBox:"0 0 466.73 532.09",paths:''},windsurf:{viewBox:"0 0 24 24",paths:''},codex:{viewBox:"0 0 24 24",paths:''},copilot:{viewBox:"0 0 24 24",paths:''},opencode:{viewBox:"0 0 24 36",paths:''},"claude-desktop":{viewBox:"0 0 24 24",paths:''},"global-agents":{viewBox:"0 0 24 24",paths:''}};function T(a,t,e=16){let s=x[t];if(!s)return;let i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.setAttribute("viewBox",s.viewBox),i.setAttribute("width",String(e)),i.setAttribute("height",String(e)),i.setAttribute("fill","none"),i.classList.add("as-tool-svg"),i.innerHTML=s.paths,a.appendChild(i)}var H=class{containerEl;store;onToggleDashboard;dashboardActive=!1;constructor(t,e,s){this.containerEl=t,this.store=e,this.onToggleDashboard=s}setDashboardActive(t){this.dashboardActive=t}render(){this.containerEl.empty(),this.containerEl.addClass("as-sidebar"),this.renderLibrarySection(),this.renderTypeSection(),this.renderToolSection(),this.renderCollectionSection(),this.store.hasSkillkit||this.renderSkillkitCta()}renderSection(t,e){let s=this.containerEl.createDiv("as-sidebar-section");s.createDiv({cls:"as-sidebar-title",text:t});for(let i of e){let n=s.createDiv("as-sidebar-item");!this.dashboardActive&&this.isActive(i.filter)&&n.addClass("is-active");let r=n.createSpan("as-sidebar-icon");(0,E.setIcon)(r,i.icon),n.createSpan({cls:"as-sidebar-label",text:i.label}),i.count!==void 0&&n.createSpan({cls:"as-sidebar-count",text:String(i.count)}),n.addEventListener("click",()=>{this.dashboardActive&&this.onToggleDashboard(),this.store.setFilter(i.filter)})}}renderTypeSection(){let t=this.store.getTypeCounts(),s=[{label:"Skills",icon:"sparkles",type:"skill"},{label:"Commands",icon:"terminal",type:"command"},{label:"Agents",icon:"bot",type:"agent"},{label:"Rules",icon:"scroll",type:"rule"}].filter(i=>t.has(i.type)).map(i=>({label:i.label,icon:i.icon,filter:{kind:"type",type:i.type},count:t.get(i.type)||0}));s.length>0&&this.renderSection("Types",s)}renderToolSection(){let t=this.store.getToolCounts(),e=v.filter(i=>i.isInstalled()&&t.has(i.id));if(e.length===0)return;let s=this.containerEl.createDiv("as-sidebar-section");s.createDiv({cls:"as-sidebar-title",text:"Tools"});for(let i of e){let n={kind:"tool",toolId:i.id},r=s.createDiv("as-sidebar-item");!this.dashboardActive&&this.isActive(n)&&r.addClass("is-active");let l=r.createSpan("as-sidebar-icon");x[i.id]?T(l,i.id,14):(0,E.setIcon)(l,i.icon),r.createSpan({cls:"as-sidebar-label",text:i.name}),r.createSpan({cls:"as-sidebar-count",text:String(t.get(i.id)||0)}),r.addEventListener("click",()=>{this.dashboardActive&&this.onToggleDashboard(),this.store.setFilter(n)})}}renderCollectionSection(){let t=this.containerEl.createDiv("as-sidebar-section");t.createDiv({cls:"as-sidebar-title",text:"Collections"});let e=new Set;for(let s of this.store.allItems)for(let i of s.collections)e.add(i);if(e.size===0){t.createDiv({cls:"as-sidebar-empty",text:"No collections yet"});return}for(let s of e){let i={kind:"collection",name:s},n=t.createDiv("as-sidebar-item");!this.dashboardActive&&this.isActive(i)&&n.addClass("is-active");let r=n.createSpan("as-sidebar-icon");(0,E.setIcon)(r,"folder"),n.createSpan({cls:"as-sidebar-label",text:s}),n.addEventListener("click",()=>{this.dashboardActive&&this.onToggleDashboard(),this.store.setFilter(i)})}}renderLibrarySection(){let t=this.containerEl.createDiv("as-sidebar-section");t.createDiv({cls:"as-sidebar-title",text:"Library"});let e=[{label:"All Skills",icon:"layers",filter:{kind:"all"}},{label:"Favorites",icon:"star",filter:{kind:"favorites"}}];for(let n of e){let r=t.createDiv("as-sidebar-item");!this.dashboardActive&&this.isActive(n.filter)&&r.addClass("is-active");let l=r.createSpan("as-sidebar-icon");(0,E.setIcon)(l,n.icon),r.createSpan({cls:"as-sidebar-label",text:n.label}),r.addEventListener("click",()=>{this.dashboardActive&&this.onToggleDashboard(),this.store.setFilter(n.filter)})}let s=t.createDiv("as-sidebar-item");this.dashboardActive&&s.addClass("is-active");let i=s.createSpan("as-sidebar-icon");(0,E.setIcon)(i,"bar-chart-2"),s.createSpan({cls:"as-sidebar-label",text:"Dashboard"}),s.addEventListener("click",()=>{this.dashboardActive||this.onToggleDashboard()})}renderSkillkitCta(){let t=this.containerEl.createDiv("as-skillkit-cta"),e=t.createDiv("as-skillkit-icon");(0,E.setIcon)(e,"bar-chart-2"),t.createDiv({cls:"as-skillkit-title",text:"Unlock analytics"}),t.createDiv({cls:"as-skillkit-desc",text:"Install skillkit to see usage stats, stale badges, and heavy skill warnings."}),t.createDiv("as-skillkit-cmd").createEl("code",{text:"npm i -g skillkit"}),t.createEl("a",{cls:"as-skillkit-link",text:"Learn more",href:"https://www.npmjs.com/package/skillkit"}).addEventListener("click",n=>{n.preventDefault(),K.shell.openExternal("https://www.npmjs.com/package/skillkit")})}isActive(t){let e=this.store.filter;return e.kind!==t.kind?!1:e.kind==="tool"&&t.kind==="tool"?e.toolId===t.toolId:e.kind==="type"&&t.kind==="type"?e.type===t.type:e.kind==="collection"&&t.kind==="collection"?e.name===t.name:!0}};var Q=require("obsidian");var $=class{containerEl;store;onSelect;selectedId=null;inputEl=null;listEl=null;constructor(t,e,s){this.containerEl=t,this.store=e,this.onSelect=s}setSelected(t){this.selectedId=t}render(){if(!this.inputEl){this.containerEl.empty(),this.containerEl.addClass("as-list");let t=this.containerEl.createDiv("as-search");this.inputEl=t.createEl("input",{type:"text",placeholder:"Search skills...",cls:"as-search-input"}),this.inputEl.addEventListener("input",()=>{this.store.setSearch(this.inputEl.value)}),this.listEl=this.containerEl.createDiv("as-list-items")}this.inputEl.value=this.store.searchQuery,this.renderList()}renderList(){if(!this.listEl)return;this.listEl.empty();let t=this.store.filteredItems;if(t.length===0){this.listEl.createDiv({cls:"as-list-empty",text:"No skills found"});return}for(let e of t)this.renderCard(this.listEl,e)}renderCard(t,e){let s=t.createDiv("as-skill-card");e.id===this.selectedId&&s.addClass("is-selected");let i=s.createDiv("as-skill-header");if(i.createSpan({cls:"as-skill-name",text:e.name}),e.isFavorite){let r=i.createSpan("as-skill-star");(0,Q.setIcon)(r,"star")}e.description&&s.createDiv({cls:"as-skill-desc",text:e.description.length>80?e.description.slice(0,80)+"...":e.description});let n=s.createDiv("as-skill-meta");n.createSpan({cls:`as-type-tag as-type-${e.type}`,text:e.type});for(let r of e.tools){let l=v.find(d=>d.id===r);if(!l)continue;let o=n.createSpan("as-tool-badge");o.title=l.name,o.setAttribute("aria-label",l.name),x[r]?(o.style.color=l.color,T(o,r,12)):(o.style.backgroundColor=l.color,o.addClass("as-tool-badge-dot"))}e.usage&&(e.usage.uses>0&&n.createSpan({cls:"as-usage-badge",text:`${e.usage.uses}`,attr:{"aria-label":`Used ${e.usage.uses} times`}}),e.usage.isStale&&n.createSpan({cls:"as-badge-stale",text:"stale"}),e.usage.isHeavy&&n.createSpan({cls:"as-badge-heavy",text:"heavy"})),s.addEventListener("click",()=>{this.selectedId=e.id,this.onSelect(e)})}};var S=require("obsidian"),it=require("fs"),at=require("electron");var q=require("child_process"),k=require("fs"),f=require("path"),Y=require("os"),b=(0,Y.homedir)(),xt=(0,f.join)(b,".skillkit","analytics.db");function Et(){let a=["/usr/local/bin","/opt/homebrew/bin",(0,f.join)(b,".local","bin"),(0,f.join)(b,".bun","bin")],t=(0,f.join)(b,".nvm","versions","node");try{for(let s of(0,k.readdirSync)(t))a.push((0,f.join)(t,s,"bin"))}catch{}let e=(0,f.join)(b,".local","share","mise","installs");for(let s of["node","bun"])try{for(let i of(0,k.readdirSync)((0,f.join)(e,s)))a.push((0,f.join)(e,s,i,"bin"))}catch{}return[...a,process.env.PATH||""].join(":")}function Ct(){let a=["/usr/local/bin/skillkit","/opt/homebrew/bin/skillkit",(0,f.join)(b,".local","bin","skillkit"),(0,f.join)(b,".bun","bin","skillkit"),(0,f.join)(b,".local","share","mise","shims","skillkit")];for(let s of a)if((0,k.existsSync)(s))return s;let t=(0,f.join)(b,".nvm","versions","node");try{for(let s of(0,k.readdirSync)(t)){let i=(0,f.join)(t,s,"bin","skillkit");if((0,k.existsSync)(i))return i}}catch{}let e=(0,f.join)(b,".local","share","mise","installs");for(let s of["node","bun"])try{for(let i of(0,k.readdirSync)((0,f.join)(e,s))){let n=(0,f.join)(e,s,i,"bin","skillkit");if((0,k.existsSync)(n))return n}}catch{}return null}var z;function X(){return z===void 0&&(z=Ct()),z}function I(){return X()!==null||(0,k.existsSync)(xt)}function _(a){let t=X();if(!t)return null;try{let e=(0,q.execSync)(`${t} ${a} --json`,{encoding:"utf-8",timeout:15e3,env:{...process.env,NO_COLOR:"1",PATH:Et()},stdio:["pipe","pipe","pipe"]}).trim(),s=e.indexOf("{"),i=e.indexOf("["),n=s===-1?i:i===-1?s:Math.min(s,i);return n===-1?null:JSON.parse(e.slice(n))}catch{return null}}function tt(){let a=new Map;if(!I())return a;let t=_("stats");if(!t?.top_skills)return a;let e=Date.now();for(let s of t.top_skills){let i=s.daily.length>0?s.daily[s.daily.length-1]?.date:null,n=null;i&&(n=Math.floor((e-new Date(i).getTime())/(1e3*60*60*24))),a.set(s.name,{uses:s.total,lastUsed:i||null,daysSinceUsed:n,isStale:n!==null&&n>30,isHeavy:!1})}return a}function et(a){if(!a)return"never";let t=Date.now()-new Date(a).getTime(),e=Math.floor(t/6e4);if(e<60)return`${e}m ago`;let s=Math.floor(e/60);if(s<24)return`${s}h ago`;let i=Math.floor(s/24);return i<30?`${i}d ago`:`${Math.floor(i/30)}mo ago`}function Tt(a){return Math.ceil(a.length/4)}function It(a){return a<1024?`${a} B`:a<1024*1024?`${(a/1024).toFixed(1)} KB`:`${(a/(1024*1024)).toFixed(1)} MB`}function st(a){return a>=1e3?`${(a/1e3).toFixed(1)}k`:String(a)}function _t(a){return new Date(a).toLocaleDateString(void 0,{month:"short",day:"numeric",year:"numeric"})}var O=class{containerEl;store;settings;saveSettings;currentItem=null;isEditing=!1;app;constructor(t,e,s,i,n){this.containerEl=t,this.store=e,this.settings=s,this.saveSettings=i,this.app=n.app}show(t){this.currentItem=t,this.isEditing=!1,this.render()}clear(){this.currentItem=null,this.containerEl.empty(),this.containerEl.addClass("as-detail");let t=this.containerEl.createDiv("as-detail-empty");(0,S.setIcon)(t.createDiv("as-detail-empty-icon"),"file-text"),t.createDiv({text:"Select a skill to view"})}render(){this.containerEl.empty(),this.containerEl.addClass("as-detail");let t=this.currentItem;if(!t)return this.clear();this.renderToolbar(t),this.isEditing?this.renderEditor(t):this.renderPreview(t)}renderToolbar(t){let e=this.containerEl.createDiv("as-detail-toolbar"),s=e.createDiv("as-toolbar-top"),i=s.createDiv("as-toolbar-left");i.createSpan({cls:"as-detail-title",text:t.name});for(let y of t.tools){let F=v.find(vt=>vt.id===y);if(!F)continue;let A=i.createSpan("as-tool-name-badge");A.style.borderColor=F.color,A.style.color=F.color,x[y]&&T(A,y,12),A.createSpan({text:F.name})}let n=s.createDiv("as-toolbar-right"),r=n.createEl("button",{cls:"as-toolbar-btn",attr:{"aria-label":"Toggle favorite"}});(0,S.setIcon)(r,t.isFavorite?"star":"star-off"),r.addEventListener("click",()=>{this.store.toggleFavorite(t.id,this.settings),this.saveSettings(),this.render()});let l=n.createEl("button",{cls:"as-toolbar-btn",attr:{"aria-label":this.isEditing?"Preview":"Edit"}});(0,S.setIcon)(l,this.isEditing?"eye":"pencil"),l.addEventListener("click",()=>{this.isEditing=!this.isEditing,this.render()});let o=n.createEl("button",{cls:"as-toolbar-btn",attr:{"aria-label":"Open in Finder"}});(0,S.setIcon)(o,"folder-open"),o.addEventListener("click",()=>{at.shell.showItemInFolder(t.filePath)});let d=e.createDiv("as-detail-meta-bar"),p=Tt(t.content),gt=t.content.length;if(d.createSpan({cls:"as-meta-item",text:It(t.fileSize)}),d.createSpan({cls:"as-meta-item",text:`${st(gt)} chars`}),d.createSpan({cls:"as-meta-item",text:`~${st(p)} tokens`}),d.createSpan({cls:"as-meta-item",text:_t(t.lastModified)}),d.createSpan({cls:"as-meta-item as-meta-type",text:t.type}),t.usage&&t.usage.uses>0){let y=e.createDiv("as-detail-usage-bar");y.createSpan({cls:"as-usage-stat",text:`${t.usage.uses} uses`}),y.createSpan({cls:"as-usage-stat",text:`last: ${et(t.usage.lastUsed)}`}),t.usage.isStale&&y.createSpan({cls:"as-badge-stale",text:"stale"}),t.usage.isHeavy&&y.createSpan({cls:"as-badge-heavy",text:"heavy"})}}renderFrontmatter(t,e){let s=Object.keys(e.frontmatter);if(s.length===0)return;let i=t.createDiv("as-frontmatter");for(let n of s){let r=e.frontmatter[n];if(r==null)continue;let l=i.createDiv("as-fm-prop");l.createSpan({cls:"as-fm-key",text:n});let o=typeof r=="object"?JSON.stringify(r):String(r);o.length>200?l.createDiv({cls:"as-fm-value-long",text:o}):l.createSpan({cls:"as-fm-value",text:o})}}renderPreview(t){let e=this.containerEl.createDiv("as-detail-body");this.renderFrontmatter(e,t);let s=e.createDiv("as-detail-preview markdown-rendered");S.MarkdownRenderer.render(this.app,t.content,s,t.filePath,this)}renderEditor(t){let e=this.containerEl.createDiv("as-detail-body as-detail-body-editor"),s=e.createEl("textarea",{cls:"as-editor-textarea"});s.value=t.content,s.spellcheck=!1,s.addEventListener("keydown",r=>{if((r.metaKey||r.ctrlKey)&&r.key==="s"&&(r.preventDefault(),this.saveFile(t,s.value)),r.key==="Tab"){r.preventDefault();let l=s.selectionStart,o=s.selectionEnd;s.value=s.value.substring(0,l)+" "+s.value.substring(o),s.selectionStart=s.selectionEnd=l+1}});let i=e.createDiv("as-save-bar");i.createEl("button",{cls:"as-save-btn",text:"Save"}).addEventListener("click",()=>{this.saveFile(t,s.value)}),i.createSpan({cls:"as-save-hint",text:"Cmd+S to save"})}saveFile(t,e){try{(0,it.writeFileSync)(t.filePath,e,"utf-8"),t.content=e,new S.Notice(`Saved ${t.name}`)}catch(s){new S.Notice(`Failed to save: ${s instanceof Error?s.message:String(s)}`)}}};var J=require("obsidian"),nt=require("electron");function Pt(){let a=_("stats"),t=_("health"),e=_("burn"),s=_("context");return{stats:a,health:t,burn:e&&e.length>0?e[0]:null,context:s}}var V=class{containerEl;constructor(t){this.containerEl=t}render(){if(this.containerEl.empty(),this.containerEl.addClass("as-dashboard"),!I()){this.renderNoSkillkit();return}let t=this.containerEl.createDiv("as-dash-loading"),e=t.createDiv("as-dash-spinner");t.createDiv({cls:"as-dash-loading-text",text:"Loading analytics..."}),setTimeout(()=>{let s=Pt();if(t.remove(),s.stats&&this.renderOverview(s.stats,s.health),s.stats&&this.renderTopSkills(s.stats),s.health||s.context){let i=this.containerEl.createDiv("as-dash-row");s.health&&this.renderHealth(s.health,i),s.context&&this.renderContext(s.context,i)}s.burn&&this.renderBurn(s.burn),s.health&&this.renderStale(s.health)},10)}renderNoSkillkit(){let t=this.containerEl.createDiv("as-dash-empty"),e=t.createDiv("as-dash-empty-icon");(0,J.setIcon)(e,"bar-chart-2"),t.createEl("h3",{text:"Dashboard requires skillkit"}),t.createEl("p",{text:"Install skillkit to unlock usage analytics, burn rate, context tax, and more."}),t.createDiv("as-dash-install-cmd").createEl("code",{text:"npm i -g @crafter/skillkit && skillkit scan"}),t.createEl("a",{cls:"as-skillkit-link",text:"Learn more",href:"https://www.npmjs.com/package/@crafter/skillkit"}).addEventListener("click",n=>{n.preventDefault(),nt.shell.openExternal("https://www.npmjs.com/package/@crafter/skillkit")})}renderOverview(t,e){let s=this.containerEl.createDiv("as-dash-section");s.createDiv({cls:"as-dash-title",text:"Overview"});let i=s.createDiv("as-dash-stats");this.statCard(i,String(t.total_invocations),"invocations","activity"),this.statCard(i,String(t.unique_skills),"active skills","sparkles"),this.statCard(i,String(e?.installed??0),"installed","package"),this.statCard(i,String(e?.usage.unused_30d??0),"stale","alert-triangle")}statCard(t,e,s,i){let n=t.createDiv("as-stat-card"),r=n.createDiv("as-stat-icon");(0,J.setIcon)(r,i),n.createDiv({cls:"as-stat-value",text:e}),n.createDiv({cls:"as-stat-label",text:s})}renderTopSkills(t){if(t.top_skills.length===0)return;let e=this.containerEl.createDiv("as-dash-section");e.createDiv({cls:"as-dash-title",text:`Top Skills (${t.period.days}d)`});let s=t.top_skills[0]?.total||1,i=e.createDiv("as-dash-bars");for(let n of t.top_skills.slice(0,10)){let r=i.createDiv("as-bar-row");r.createSpan({cls:"as-bar-name",text:n.name});let o=r.createDiv("as-bar-wrap").createDiv("as-bar-fill");o.style.width=`${n.total/s*100}%`,r.createSpan({cls:"as-bar-count",text:String(n.total)})}}renderHealth(t,e){let s=(e||this.containerEl).createDiv("as-dash-section");s.createDiv({cls:"as-dash-title",text:"Health"});let i=t.usage.used_30d+t.usage.unused_30d,n=i>0?Math.round(t.usage.used_30d/i*100):0,r=s.createDiv("as-dash-health-row"),l=r.createDiv("as-donut");l.style.setProperty("--pct",`${n}`),l.createDiv({cls:"as-donut-label",text:`${n}%`}),l.createDiv({cls:"as-donut-sub",text:"active"});let o=r.createDiv("as-health-details");o.createDiv({cls:"as-health-line",text:`${t.usage.used_30d} used in 30d`}),o.createDiv({cls:"as-health-line as-health-warn",text:`${t.usage.unused_30d} never triggered`});let p=o.createDiv("as-budget-bar").createDiv("as-budget-fill");p.style.width=`${t.metadata.pct}%`,t.metadata.pct>80&&p.addClass("as-budget-over"),o.createDiv({cls:"as-health-line",text:`Metadata budget: ${t.metadata.pct}%`})}renderBurn(t){let e=this.containerEl.createDiv("as-dash-section");e.createDiv({cls:"as-dash-title",text:`Burn Rate \u2014 ${t.agent} (${t.period.days}d)`});let s=e.createDiv("as-dash-stats as-dash-stats-sm");if(this.statCard(s,`$${t.cost.total.toFixed(0)}`,"total cost","flame"),this.statCard(s,`$${(t.cost.total/(t.period.days||1)).toFixed(0)}`,"daily avg","trending-up"),this.statCard(s,`${(t.period.sessions||0).toLocaleString()}`,"sessions","terminal"),this.statCard(s,`${((t.period.api_calls||0)/1e3).toFixed(0)}k`,"API calls","zap"),t.by_model&&t.by_model.length>0){let l=e.createDiv("as-model-breakdown");for(let o of t.by_model.slice(0,4)){let d=l.createDiv("as-model-row");d.createSpan({cls:"as-model-name",text:o.model}),d.createSpan({cls:"as-model-calls",text:`${o.apiCalls.toLocaleString()} calls`}),o.costUsd>0&&d.createSpan({cls:"as-model-cost",text:`$${o.costUsd.toFixed(0)}`})}}let i=t.by_day.slice(-14);if(i.length===0)return;let n=Math.max(...i.map(l=>l.costUsd),1),r=e.createDiv("as-burn-chart");for(let l of i){let o=r.createDiv("as-burn-col"),d=o.createDiv("as-burn-bar"),p=Math.max(2,l.costUsd/n*100);d.style.height=`${p}%`,d.title=`${l.date}: $${l.costUsd.toFixed(0)}`,o.createDiv({cls:"as-burn-date",text:l.date.slice(8)})}}renderContext(t,e){let s=(e||this.containerEl).createDiv("as-dash-section");s.createDiv({cls:"as-dash-title",text:"Context Tax"});let i=t.always_loaded.total_tokens,n=[{label:"CLAUDE.md",tokens:t.always_loaded.claude_md_tokens,cls:"as-ctx-claude"},{label:"Skills metadata",tokens:t.always_loaded.skill_metadata_tokens,cls:"as-ctx-skills"},{label:"Memory",tokens:t.always_loaded.memory_tokens,cls:"as-ctx-memory"}],r=s.createDiv("as-ctx-bar");for(let d of n){let p=r.createDiv(`as-ctx-part ${d.cls}`);p.style.width=`${d.tokens/i*100}%`,p.title=`${d.label}: ${(d.tokens/1e3).toFixed(1)}k tokens`}let l=s.createDiv("as-ctx-legend");for(let d of n){let p=l.createDiv("as-ctx-legend-item");p.createSpan({cls:`as-ctx-dot ${d.cls}`}),p.createSpan({text:`${d.label}: ${(d.tokens/1e3).toFixed(1)}k`})}let o=s.createDiv("as-ctx-costs");o.createDiv({text:`Per session (cached): $${t.session_estimate.with_cache.toFixed(2)}`}),o.createDiv({text:`Without cache: $${t.session_estimate.without_cache.toFixed(2)}`}),o.createDiv({cls:"as-ctx-savings",text:`Cache saves ${t.session_estimate.savings_pct.toFixed(0)}%`})}renderStale(t){if(t.usage.never_used.length===0)return;let e=this.containerEl.createDiv("as-dash-section");e.createDiv({cls:"as-dash-title",text:`Stale Skills (${t.usage.unused_30d})`});let s=e.createDiv("as-stale-list");for(let n of t.usage.never_used.slice(0,20))s.createDiv("as-stale-item").createSpan({text:n});t.usage.never_used.length>20&&s.createDiv({cls:"as-stale-more",text:`+${t.usage.never_used.length-20} more`});let i=e.createDiv("as-stale-hint");i.createEl("code",{text:"skillkit prune"}),i.createSpan({text:" to remove unused skills"})}};var L="agentfiles-view",B=class extends rt.ItemView{store;settings;saveSettings;sidebarPanel;listPanel;detailPanel;dashboardPanel;sidebarEl;listEl;detailEl;dashboardEl;isDashboard=!1;updateRef=null;constructor(t,e,s,i){super(t),this.store=e,this.settings=s,this.saveSettings=i}getViewType(){return L}getDisplayText(){return"Agentfiles"}getIcon(){return"cpu"}onOpen(){let t=this.contentEl;t.empty(),t.addClass("as-container"),this.sidebarEl=t.createDiv("as-panel as-panel-sidebar"),this.listEl=t.createDiv("as-panel as-panel-list"),this.detailEl=t.createDiv("as-panel as-panel-detail"),this.dashboardEl=t.createDiv("as-panel as-panel-dashboard"),this.dashboardEl.style.display="none",this.sidebarPanel=new H(this.sidebarEl,this.store,()=>this.toggleDashboard()),this.listPanel=new $(this.listEl,this.store,e=>this.onSelectItem(e)),this.detailPanel=new O(this.detailEl,this.store,this.settings,this.saveSettings,this),this.dashboardPanel=new V(this.dashboardEl),this.updateRef=this.store.on("updated",()=>this.renderAll()),this.renderAll()}toggleDashboard(){this.isDashboard=!this.isDashboard,this.isDashboard?(this.listEl.style.display="none",this.detailEl.style.display="none",this.dashboardEl.style.display="block",this.dashboardPanel.render()):(this.listEl.style.display="",this.detailEl.style.display="",this.dashboardEl.style.display="none"),this.sidebarPanel.setDashboardActive(this.isDashboard),this.sidebarPanel.render()}renderAll(){this.sidebarPanel.render(),this.isDashboard||(this.listPanel.render(),this.store.filteredItems.length||this.detailPanel.clear())}onSelectItem(t){this.isDashboard&&this.toggleDashboard(),this.listPanel.setSelected(t.id),this.listPanel.render(),this.detailPanel.show(t)}onClose(){this.updateRef&&this.store.offref(this.updateRef)}};var ut=require("obsidian");var u=require("fs"),g=require("path"),lt=require("obsidian"),ct=require("crypto");var ot=new Set(["readme.md","license","license.md","changelog.md",".ds_store","thumbs.db"]);function Lt(a){return(0,ct.createHash)("sha256").update(a).digest("hex").slice(0,12)}function Mt(a){let t=a.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);if(!t)return{frontmatter:{},content:a};try{let e=(0,lt.parseYaml)(t[1]);return{frontmatter:typeof e=="object"&&e?e:{},content:t[2]}}catch{return{frontmatter:{},content:a}}}function Ft(a,t,e,s){if(typeof a.name=="string"&&a.name)return a.name;let i=(0,g.basename)(e,(0,g.extname)(e));if(i==="SKILL")return(0,g.basename)((0,g.join)(e,".."));if(s==="flat-md"||s==="mdc")return i;let n=t.match(/^#\s+(.+)$/m);return n?n[1].trim():i}function At(a,t,e){if(!(0,u.existsSync)(a))return[];let s=[];for(let i of(0,u.readdirSync)(a,{withFileTypes:!0})){if(!i.isDirectory()&&!i.isSymbolicLink())continue;let n=(0,g.join)(a,i.name,"SKILL.md");if(!(0,u.existsSync)(n))continue;let r=M(n,t,e);r&&s.push(r)}return s}function Ht(a,t,e){if(!(0,u.existsSync)(a))return[];let s=[];for(let i of(0,u.readdirSync)(a,{withFileTypes:!0})){if(i.isDirectory()||i.isSymbolicLink()){let l=(0,g.join)(a,i.name,"SKILL.md");if((0,u.existsSync)(l)){let p=M(l,t,e);p&&s.push(p);continue}let o=(0,u.readdirSync)((0,g.join)(a,i.name)).filter(p=>p.endsWith(".md")&&!ot.has(p.toLowerCase())),d=o.find(p=>p.toLowerCase()===`${i.name.toLowerCase()}.md`)||o[0];if(d){let p=M((0,g.join)(a,i.name,d),t,e);p&&s.push(p)}continue}let n=i.name.toLowerCase();if(!n.endsWith(".md")||ot.has(n))continue;let r=M((0,g.join)(a,i.name),t,e,"flat-md");r&&s.push(r)}return s}function $t(a,t,e){if(!(0,u.existsSync)(a))return[];let s=[];for(let i of(0,u.readdirSync)(a,{withFileTypes:!0})){if(!i.name.endsWith(".mdc")&&!i.name.endsWith(".md")||i.isDirectory())continue;let n=M((0,g.join)(a,i.name),t,e,"mdc");n&&s.push(n)}return s}function M(a,t,e,s="directory-with-skillmd"){try{let i=(0,u.readFileSync)(a,"utf-8"),n=(0,u.statSync)(a),{frontmatter:r,content:l}=Mt(i),o=Ft(r,l,a,s),d=typeof r.description=="string"?r.description:"",p;try{p=(0,u.realpathSync)(a)}catch{p=a}return{id:Lt(p),name:o,description:d,type:t,tools:[e],filePath:a,realPath:p,dirPath:(0,g.join)(a,".."),content:i,frontmatter:r,lastModified:n.mtimeMs,fileSize:n.size,isFavorite:!1,collections:[]}}catch{return null}}function dt(a,t){switch(a.pattern){case"directory-with-skillmd":return At(a.baseDir,a.type,t);case"flat-md":return Ht(a.baseDir,a.type,t);case"mdc":return $t(a.baseDir,a.type,t)}}function Ot(a,t){let e=[],s=[{sub:".claude/skills",type:"skill",pattern:"directory-with-skillmd"},{sub:".claude/commands",type:"command",pattern:"flat-md"},{sub:".claude/agents",type:"agent",pattern:"flat-md"},{sub:".cursor/skills",type:"skill",pattern:"directory-with-skillmd"},{sub:".codex/skills",type:"skill",pattern:"directory-with-skillmd"}];for(let i of s){let n=(0,g.join)(a,i.sub);if(!(0,u.existsSync)(n))continue;let r={baseDir:n,type:i.type,pattern:i.pattern};e.push(...dt(r,t))}return e}function ht(a){let t=new Map,e=new Map;function s(i,n){let r=t.get(i.id);if(r){r.tools.includes(n)||r.tools.push(n);return}let l=e.get(i.name);if(l){let o=t.get(l);o&&!o.tools.includes(n)&&o.tools.push(n);return}i.isFavorite=a.favorites.includes(i.id);for(let[o,d]of Object.entries(a.collections))d.includes(i.id)&&i.collections.push(o);t.set(i.id,i),e.set(i.name,i.id)}for(let i of v){if(!i.isInstalled())continue;let n=a.tools[i.id];if(n&&!n.enabled)continue;let r=[...i.paths,...i.agentPaths];for(let l of r)for(let o of dt(l,i.id))s(o,i.id)}for(let i of a.customScanPaths)if((0,u.existsSync)(i))for(let n of Ot(i,"claude-code"))s(n,"claude-code");return t}function pt(){let a=[];for(let t of v)if(t.isInstalled())for(let e of[...t.paths,...t.agentPaths])(0,u.existsSync)(e.baseDir)&&a.push(e.baseDir);return a}var N=class extends ut.Events{items=new Map;_filter={kind:"all"};_searchQuery="";get filter(){return this._filter}get searchQuery(){return this._searchQuery}get allItems(){return Array.from(this.items.values())}get filteredItems(){let t=this.allItems;switch(this._filter.kind){case"favorites":t=t.filter(e=>e.isFavorite);break;case"tool":t=t.filter(e=>e.tools.includes(this._filter.toolId));break;case"type":t=t.filter(e=>e.type===this._filter.type);break;case"collection":t=t.filter(e=>e.collections.includes(this._filter.name));break}if(this._searchQuery){let e=this._searchQuery.toLowerCase();t=t.filter(s=>s.name.toLowerCase().includes(e)||s.description.toLowerCase().includes(e)||s.content.toLowerCase().includes(e))}return t.sort((e,s)=>e.name.localeCompare(s.name))}getItem(t){return this.items.get(t)}get hasSkillkit(){return I()}refresh(t){this.items=ht(t),this.enrichWithSkillkit(),this.trigger("updated")}enrichWithSkillkit(){if(!I())return;let t=tt();for(let e of this.items.values()){let s=e.filePath.split("/").slice(-2,-1)[0],i=e.name.toLowerCase().replace(/\s+/g,"-"),n=t.get(e.name)||t.get(s)||t.get(i);n?(n.isHeavy=e.content.length>5e3,e.usage=n):e.usage={uses:0,lastUsed:null,daysSinceUsed:null,isStale:!1,isHeavy:e.content.length>5e3}}}setFilter(t){this._filter=t,this.trigger("updated")}setSearch(t){this._searchQuery=t,this.trigger("updated")}toggleFavorite(t,e){let s=this.items.get(t);s&&(s.isFavorite=!s.isFavorite,s.isFavorite?e.favorites.includes(t)||e.favorites.push(t):e.favorites=e.favorites.filter(i=>i!==t),this.trigger("updated"))}getToolCounts(){let t=new Map;for(let e of this.items.values())for(let s of e.tools)t.set(s,(t.get(s)||0)+1);return t}getTypeCounts(){let t=new Map;for(let e of this.items.values())t.set(e.type,(t.get(e.type)||0)+1);return t}};var mt=require("fs"),W=class{watchers=[];debounceTimer=null;debounceMs;onChange;constructor(t,e){this.debounceMs=t,this.onChange=e}watchPaths(t){this.close();for(let e of t)try{let s=(0,mt.watch)(e,{recursive:!0},()=>this.scheduleUpdate());this.watchers.push(s)}catch{}}scheduleUpdate(){this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.onChange()},this.debounceMs)}close(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null);for(let t of this.watchers)try{t.close()}catch{}this.watchers=[]}};var D=require("obsidian");var j=class extends D.PluginSettingTab{plugin;constructor(t,e){super(t,e),this.plugin=e}display(){let{containerEl:t}=this;t.empty(),new D.Setting(t).setName("Agentfiles").setHeading(),new D.Setting(t).setName("File watching").setDesc("Automatically detect changes to skill files").addToggle(e=>e.setValue(this.plugin.settings.watchEnabled).onChange(async s=>{this.plugin.settings.watchEnabled=s,await this.plugin.saveSettings(),this.plugin.restartWatcher()})),new D.Setting(t).setName("Watch debounce (ms)").setDesc("Delay before re-scanning after file changes").addText(e=>e.setValue(String(this.plugin.settings.watchDebounceMs)).onChange(async s=>{let i=parseInt(s);!isNaN(i)&&i>=100&&(this.plugin.settings.watchDebounceMs=i,await this.plugin.saveSettings())})),new D.Setting(t).setName("Tools").setHeading();for(let e of v){let s=e.isInstalled(),i=this.plugin.settings.tools[e.id]||{enabled:!0,customPaths:[]};new D.Setting(t).setName(e.name).setDesc(s?"Installed":"Not detected").addToggle(n=>n.setValue(s&&i.enabled).setDisabled(!s).onChange(async r=>{this.plugin.settings.tools[e.id]={...i,enabled:r},await this.plugin.saveSettings(),this.plugin.refreshStore()}))}}};var G={tools:{},watchEnabled:!0,watchDebounceMs:500,favorites:[],collections:{},customScanPaths:[]};var U=class extends ft.Plugin{settings=G;store=new N;watcher=null;async onload(){await this.loadSettings(),this.addVaultPath(),this.registerView(L,t=>new B(t,this.store,this.settings,()=>this.saveSettings())),this.addRibbonIcon("cpu","Agentfiles",()=>this.activateView()),this.addCommand({id:"open",name:"Open",callback:()=>this.activateView()}),this.addSettingTab(new j(this.app,this)),this.refreshStore(),this.startWatcher()}addVaultPath(){let t=this.app.vault.adapter;if(!t.getBasePath)return;let e=t.getBasePath();this.settings.customScanPaths.includes(e)||this.settings.customScanPaths.push(e)}onunload(){this.stopWatcher()}refreshStore(){this.store.refresh(this.settings)}startWatcher(){this.settings.watchEnabled&&(this.watcher=new W(this.settings.watchDebounceMs,()=>this.refreshStore()),this.watcher.watchPaths(pt()))}stopWatcher(){this.watcher&&(this.watcher.close(),this.watcher=null)}restartWatcher(){this.stopWatcher(),this.startWatcher()}async activateView(){let t=this.app.workspace.getLeavesOfType(L);if(t.length>0){this.app.workspace.revealLeaf(t[0]);return}let e=this.app.workspace.getLeaf("tab");await e.setViewState({type:L,active:!0}),this.app.workspace.revealLeaf(e)}async loadSettings(){this.settings=Object.assign({},G,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}}; +var z=Object.defineProperty;var yt=Object.getOwnPropertyDescriptor;var Dt=Object.getOwnPropertyNames;var wt=Object.prototype.hasOwnProperty;var xt=(a,t)=>{for(var e in t)z(a,e,{get:t[e],enumerable:!0})},Et=(a,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Dt(t))!wt.call(a,i)&&i!==e&&z(a,i,{get:()=>t[i],enumerable:!(s=yt(t,i))||s.enumerable});return a};var Ct=a=>Et(z({},"__esModule",{value:!0}),a);var Ut={};xt(Ut,{default:()=>R});module.exports=Ct(Ut);var bt=require("obsidian");var ct=require("obsidian");var E=require("obsidian"),Y=require("electron");var q=require("os"),f=require("fs"),c=require("path"),h=(0,q.homedir)(),C=process.env.XDG_CONFIG_HOME||(0,c.join)(h,".config");function _(a){return(0,f.existsSync)(`/Applications/${a}.app`)||(0,f.existsSync)((0,c.join)(h,"Applications",`${a}.app`))}function w(a){let t=[`/usr/local/bin/${a}`,`/opt/homebrew/bin/${a}`,(0,c.join)(h,".local","bin",a)];for(let s of t)if((0,f.existsSync)(s))return!0;let e=(0,c.join)(h,".nvm","versions","node");try{let{readdirSync:s}=require("fs");for(let i of s(e))if((0,f.existsSync)((0,c.join)(e,i,"bin",a)))return!0}catch{}return!1}var v=[{id:"claude-code",name:"Claude Code",color:"#f97316",icon:"brain",paths:[{baseDir:(0,c.join)(h,".claude","skills"),type:"skill",pattern:"directory-with-skillmd"},{baseDir:(0,c.join)(h,".claude","commands"),type:"command",pattern:"flat-md"}],agentPaths:[{baseDir:(0,c.join)(h,".claude","agents"),type:"agent",pattern:"flat-md"}],isInstalled:()=>(0,f.existsSync)((0,c.join)(h,".claude","settings.json"))||(0,f.existsSync)((0,c.join)(h,".claude","CLAUDE.md"))||w("claude")},{id:"cursor",name:"Cursor",color:"#3b82f6",icon:"mouse-pointer",paths:[{baseDir:(0,c.join)(h,".cursor","skills"),type:"skill",pattern:"directory-with-skillmd"},{baseDir:(0,c.join)(h,".cursor","rules"),type:"rule",pattern:"flat-md"}],agentPaths:[{baseDir:(0,c.join)(h,".cursor","agents"),type:"agent",pattern:"flat-md"}],isInstalled:()=>_("Cursor")||(0,f.existsSync)((0,c.join)(h,".cursor","argv.json"))},{id:"windsurf",name:"Windsurf",color:"#14b8a6",icon:"wind",paths:[{baseDir:(0,c.join)(h,".codeium","windsurf","memories"),type:"memory",pattern:"flat-md"},{baseDir:(0,c.join)(h,".windsurf","rules"),type:"rule",pattern:"flat-md"}],agentPaths:[],isInstalled:()=>_("Windsurf")||(0,f.existsSync)((0,c.join)(h,".codeium","windsurf","argv.json"))},{id:"codex",name:"Codex",color:"#22c55e",icon:"book",paths:[{baseDir:(0,c.join)(h,".codex","skills"),type:"skill",pattern:"directory-with-skillmd"},{baseDir:(0,c.join)(h,".codex","prompts"),type:"command",pattern:"flat-md"},{baseDir:(0,c.join)(h,".codex","memories"),type:"memory",pattern:"flat-md"}],agentPaths:[{baseDir:(0,c.join)(h,".codex","agents"),type:"agent",pattern:"flat-md"}],isInstalled:()=>(0,f.existsSync)((0,c.join)(h,".codex","config.toml"))||(0,f.existsSync)((0,c.join)(h,".codex","auth.json"))||w("codex")},{id:"copilot",name:"Copilot",color:"#a855f7",icon:"plane",paths:[{baseDir:(0,c.join)(h,".copilot","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>(0,f.existsSync)((0,c.join)(h,".copilot"))||w("copilot")},{id:"amp",name:"Amp",color:"#ec4899",icon:"zap",paths:[{baseDir:(0,c.join)(C,"amp","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>(0,f.existsSync)((0,c.join)(C,"amp","config.json"))||(0,f.existsSync)((0,c.join)(C,"amp","settings.json"))||w("amp")},{id:"opencode",name:"OpenCode",color:"#ef4444",icon:"terminal",paths:[{baseDir:(0,c.join)(C,"opencode","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>_("OpenCode")||(0,f.existsSync)((0,c.join)(C,"opencode","opencode.json"))||(0,f.existsSync)((0,c.join)(C,"opencode","opencode.jsonc"))||w("opencode")},{id:"pi",name:"Pi",color:"#06b6d4",icon:"sparkles",paths:[{baseDir:(0,c.join)(h,".pi","agent","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>w("pi")},{id:"antigravity",name:"Antigravity",color:"#ef4444",icon:"arrow-up-circle",paths:[{baseDir:(0,c.join)(h,".gemini","antigravity","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>_("Antigravity")||(0,f.existsSync)((0,c.join)(h,".gemini","antigravity","skills"))||w("antigravity")},{id:"claude-desktop",name:"Claude Desktop",color:"#f97316",icon:"monitor",paths:[],agentPaths:[],isInstalled:()=>_("Claude")},{id:"global-agents",name:"Global",color:"#a3e635",icon:"globe",paths:[{baseDir:(0,c.join)(h,".agents","skills"),type:"skill",pattern:"directory-with-skillmd"}],agentPaths:[],isInstalled:()=>(0,f.existsSync)((0,c.join)(h,".agents","skills"))},{id:"aider",name:"Aider",color:"#eab308",icon:"wrench",paths:[],agentPaths:[],isInstalled:()=>w("aider")}];var x={"claude-code":{viewBox:"0 0 24 24",paths:''},cursor:{viewBox:"0 0 466.73 532.09",paths:''},windsurf:{viewBox:"0 0 24 24",paths:''},codex:{viewBox:"0 0 24 24",paths:''},copilot:{viewBox:"0 0 24 24",paths:''},opencode:{viewBox:"0 0 24 36",paths:''},"claude-desktop":{viewBox:"0 0 24 24",paths:''},"global-agents":{viewBox:"0 0 24 24",paths:''}};function P(a,t,e=16){let s=x[t];if(!s)return;let i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.setAttribute("viewBox",s.viewBox),i.setAttribute("width",String(e)),i.setAttribute("height",String(e)),i.setAttribute("fill","none"),i.classList.add("as-tool-svg"),i.innerHTML=s.paths,a.appendChild(i)}var j=class{containerEl;store;onToggleDashboard;dashboardActive=!1;constructor(t,e,s){this.containerEl=t,this.store=e,this.onToggleDashboard=s}setDashboardActive(t){this.dashboardActive=t}render(){this.containerEl.empty(),this.containerEl.addClass("as-sidebar"),this.renderLibrarySection(),this.renderTypeSection(),this.renderToolSection(),this.renderProjectSection(),this.renderCollectionSection(),this.store.hasSkillkit||this.renderSkillkitCta()}renderSection(t,e){let s=this.containerEl.createDiv("as-sidebar-section");s.createDiv({cls:"as-sidebar-title",text:t});for(let i of e){let n=s.createDiv("as-sidebar-item");!this.dashboardActive&&this.isActive(i.filter)&&n.addClass("is-active");let r=n.createSpan("as-sidebar-icon");(0,E.setIcon)(r,i.icon),n.createSpan({cls:"as-sidebar-label",text:i.label}),i.count!==void 0&&n.createSpan({cls:"as-sidebar-count",text:String(i.count)}),n.addEventListener("click",()=>{this.dashboardActive&&this.onToggleDashboard(),this.store.setFilter(i.filter)})}}renderTypeSection(){let t=this.store.getTypeCounts(),s=[{label:"Skills",icon:"sparkles",type:"skill"},{label:"Commands",icon:"terminal",type:"command"},{label:"Agents",icon:"bot",type:"agent"},{label:"Rules",icon:"scroll",type:"rule"}].filter(i=>t.has(i.type)).map(i=>({label:i.label,icon:i.icon,filter:{kind:"type",type:i.type},count:t.get(i.type)||0}));s.length>0&&this.renderSection("Types",s)}renderToolSection(){let t=this.store.getToolCounts(),e=v.filter(i=>i.isInstalled()&&t.has(i.id));if(e.length===0)return;let s=this.containerEl.createDiv("as-sidebar-section");s.createDiv({cls:"as-sidebar-title",text:"Tools"});for(let i of e){let n={kind:"tool",toolId:i.id},r=s.createDiv("as-sidebar-item");!this.dashboardActive&&this.isActive(n)&&r.addClass("is-active");let o=r.createSpan("as-sidebar-icon");x[i.id]?P(o,i.id,14):(0,E.setIcon)(o,i.icon),r.createSpan({cls:"as-sidebar-label",text:i.name}),r.createSpan({cls:"as-sidebar-count",text:String(t.get(i.id)||0)}),r.addEventListener("click",()=>{this.dashboardActive&&this.onToggleDashboard(),this.store.setFilter(n)})}}renderProjectSection(){let t=this.store.getProjectCounts();if(t.size===0)return;let e=[];for(let[s,i]of t)e.push({label:s,icon:"folder-git-2",filter:{kind:"project",project:s},count:i});e.sort((s,i)=>s.label.localeCompare(i.label)),this.renderSection("Projects",e)}renderCollectionSection(){let t=this.containerEl.createDiv("as-sidebar-section");t.createDiv({cls:"as-sidebar-title",text:"Collections"});let e=new Set;for(let s of this.store.allItems)for(let i of s.collections)e.add(i);if(e.size===0){t.createDiv({cls:"as-sidebar-empty",text:"No collections yet"});return}for(let s of e){let i={kind:"collection",name:s},n=t.createDiv("as-sidebar-item");!this.dashboardActive&&this.isActive(i)&&n.addClass("is-active");let r=n.createSpan("as-sidebar-icon");(0,E.setIcon)(r,"folder"),n.createSpan({cls:"as-sidebar-label",text:s}),n.addEventListener("click",()=>{this.dashboardActive&&this.onToggleDashboard(),this.store.setFilter(i)})}}renderLibrarySection(){let t=this.containerEl.createDiv("as-sidebar-section");t.createDiv({cls:"as-sidebar-title",text:"Library"});let e=[{label:"All Skills",icon:"layers",filter:{kind:"all"}},{label:"Favorites",icon:"star",filter:{kind:"favorites"}}];for(let n of e){let r=t.createDiv("as-sidebar-item");!this.dashboardActive&&this.isActive(n.filter)&&r.addClass("is-active");let o=r.createSpan("as-sidebar-icon");(0,E.setIcon)(o,n.icon),r.createSpan({cls:"as-sidebar-label",text:n.label}),r.addEventListener("click",()=>{this.dashboardActive&&this.onToggleDashboard(),this.store.setFilter(n.filter)})}let s=t.createDiv("as-sidebar-item");this.dashboardActive&&s.addClass("is-active");let i=s.createSpan("as-sidebar-icon");(0,E.setIcon)(i,"bar-chart-2"),s.createSpan({cls:"as-sidebar-label",text:"Dashboard"}),s.addEventListener("click",()=>{this.dashboardActive||this.onToggleDashboard()})}renderSkillkitCta(){let t=this.containerEl.createDiv("as-skillkit-cta"),e=t.createDiv("as-skillkit-icon");(0,E.setIcon)(e,"bar-chart-2"),t.createDiv({cls:"as-skillkit-title",text:"Unlock analytics"}),t.createDiv({cls:"as-skillkit-desc",text:"Install skillkit to see usage stats, stale badges, and heavy skill warnings."}),t.createDiv("as-skillkit-cmd").createEl("code",{text:"npm i -g skillkit"}),t.createEl("a",{cls:"as-skillkit-link",text:"Learn more",href:"https://www.npmjs.com/package/skillkit"}).addEventListener("click",n=>{n.preventDefault(),Y.shell.openExternal("https://www.npmjs.com/package/skillkit")})}isActive(t){let e=this.store.filter;return e.kind!==t.kind?!1:e.kind==="tool"&&t.kind==="tool"?e.toolId===t.toolId:e.kind==="type"&&t.kind==="type"?e.type===t.type:e.kind==="collection"&&t.kind==="collection"?e.name===t.name:e.kind==="project"&&t.kind==="project"?e.project===t.project:!0}};var X=require("obsidian");var A=class{containerEl;store;onSelect;selectedId=null;inputEl=null;listEl=null;constructor(t,e,s){this.containerEl=t,this.store=e,this.onSelect=s}setSelected(t){this.selectedId=t}render(){if(!this.inputEl){this.containerEl.empty(),this.containerEl.addClass("as-list");let t=this.containerEl.createDiv("as-search");this.inputEl=t.createEl("input",{type:"text",placeholder:"Search skills...",cls:"as-search-input"}),this.inputEl.addEventListener("input",()=>{this.store.setSearch(this.inputEl.value)}),this.listEl=this.containerEl.createDiv("as-list-items")}this.inputEl.value=this.store.searchQuery,this.renderList()}renderList(){if(!this.listEl)return;this.listEl.empty();let t=this.store.filteredItems;if(t.length===0){this.listEl.createDiv({cls:"as-list-empty",text:"No skills found"});return}for(let e of t)this.renderCard(this.listEl,e)}renderCard(t,e){let s=t.createDiv("as-skill-card");e.id===this.selectedId&&s.addClass("is-selected");let i=s.createDiv("as-skill-header");if(i.createSpan({cls:"as-skill-name",text:e.name}),e.isFavorite){let r=i.createSpan("as-skill-star");(0,X.setIcon)(r,"star")}e.description&&s.createDiv({cls:"as-skill-desc",text:e.description.length>80?e.description.slice(0,80)+"...":e.description});let n=s.createDiv("as-skill-meta");n.createSpan({cls:`as-type-tag as-type-${e.type}`,text:e.type});for(let r of e.tools){let o=v.find(d=>d.id===r);if(!o)continue;let l=n.createSpan("as-tool-badge");l.title=o.name,l.setAttribute("aria-label",o.name),x[r]?(l.style.color=o.color,P(l,r,12)):(l.style.backgroundColor=o.color,l.addClass("as-tool-badge-dot"))}e.usage&&(e.usage.uses>0&&n.createSpan({cls:"as-usage-badge",text:`${e.usage.uses}`,attr:{"aria-label":`Used ${e.usage.uses} times`}}),e.usage.isStale&&n.createSpan({cls:"as-badge-stale",text:"stale"}),e.usage.isHeavy&&n.createSpan({cls:"as-badge-heavy",text:"heavy"})),s.addEventListener("click",()=>{this.selectedId=e.id,this.onSelect(e)})}};var y=require("obsidian"),rt=require("fs"),ot=require("electron");var tt=require("child_process"),k=require("fs"),g=require("path"),et=require("os"),S=(0,et.homedir)(),Pt=(0,g.join)(S,".skillkit","analytics.db");function Tt(){let a=["/usr/local/bin","/opt/homebrew/bin",(0,g.join)(S,".local","bin"),(0,g.join)(S,".bun","bin")],t=(0,g.join)(S,".nvm","versions","node");try{for(let s of(0,k.readdirSync)(t))a.push((0,g.join)(t,s,"bin"))}catch{}let e=(0,g.join)(S,".local","share","mise","installs");for(let s of["node","bun"])try{for(let i of(0,k.readdirSync)((0,g.join)(e,s)))a.push((0,g.join)(e,s,i,"bin"))}catch{}return[...a,process.env.PATH||""].join(":")}function It(){let a=["/usr/local/bin/skillkit","/opt/homebrew/bin/skillkit",(0,g.join)(S,".local","bin","skillkit"),(0,g.join)(S,".bun","bin","skillkit"),(0,g.join)(S,".local","share","mise","shims","skillkit")];for(let s of a)if((0,k.existsSync)(s))return s;let t=(0,g.join)(S,".nvm","versions","node");try{for(let s of(0,k.readdirSync)(t)){let i=(0,g.join)(t,s,"bin","skillkit");if((0,k.existsSync)(i))return i}}catch{}let e=(0,g.join)(S,".local","share","mise","installs");for(let s of["node","bun"])try{for(let i of(0,k.readdirSync)((0,g.join)(e,s))){let n=(0,g.join)(e,s,i,"bin","skillkit");if((0,k.existsSync)(n))return n}}catch{}return null}var J;function st(){return J===void 0&&(J=It()),J}function T(){return st()!==null||(0,k.existsSync)(Pt)}function I(a){let t=st();if(!t)return null;try{let e=(0,tt.execSync)(`${t} ${a} --json`,{encoding:"utf-8",timeout:15e3,env:{...process.env,NO_COLOR:"1",PATH:Tt()},stdio:["pipe","pipe","pipe"]}).trim(),s=e.indexOf("{"),i=e.indexOf("["),n=s===-1?i:i===-1?s:Math.min(s,i);return n===-1?null:JSON.parse(e.slice(n))}catch{return null}}function it(){let a=new Map;if(!T())return a;let t=I("stats");if(!t?.top_skills)return a;let e=Date.now();for(let s of t.top_skills){let i=s.daily.length>0?s.daily[s.daily.length-1]?.date:null,n=null;i&&(n=Math.floor((e-new Date(i).getTime())/(1e3*60*60*24))),a.set(s.name,{uses:s.total,lastUsed:i||null,daysSinceUsed:n,isStale:n!==null&&n>30,isHeavy:!1})}return a}function at(a){if(!a)return"never";let t=Date.now()-new Date(a).getTime(),e=Math.floor(t/6e4);if(e<60)return`${e}m ago`;let s=Math.floor(e/60);if(s<24)return`${s}h ago`;let i=Math.floor(s/24);return i<30?`${i}d ago`:`${Math.floor(i/30)}mo ago`}function _t(a){return Math.ceil(a.length/4)}function Lt(a){return a<1024?`${a} B`:a<1024*1024?`${(a/1024).toFixed(1)} KB`:`${(a/(1024*1024)).toFixed(1)} MB`}function nt(a){return a>=1e3?`${(a/1e3).toFixed(1)}k`:String(a)}function Mt(a){return new Date(a).toLocaleDateString(void 0,{month:"short",day:"numeric",year:"numeric"})}var $=class{containerEl;store;settings;saveSettings;currentItem=null;isEditing=!1;app;constructor(t,e,s,i,n){this.containerEl=t,this.store=e,this.settings=s,this.saveSettings=i,this.app=n.app}show(t){this.currentItem=t,this.isEditing=!1,this.render()}clear(){this.currentItem=null,this.containerEl.empty(),this.containerEl.addClass("as-detail");let t=this.containerEl.createDiv("as-detail-empty");(0,y.setIcon)(t.createDiv("as-detail-empty-icon"),"file-text"),t.createDiv({text:"Select a skill to view"})}render(){this.containerEl.empty(),this.containerEl.addClass("as-detail");let t=this.currentItem;if(!t)return this.clear();this.renderToolbar(t),this.isEditing?this.renderEditor(t):this.renderPreview(t)}renderToolbar(t){let e=this.containerEl.createDiv("as-detail-toolbar"),s=e.createDiv("as-toolbar-top"),i=s.createDiv("as-toolbar-left");i.createSpan({cls:"as-detail-title",text:t.name});for(let D of t.tools){let F=v.find(kt=>kt.id===D);if(!F)continue;let H=i.createSpan("as-tool-name-badge");H.style.borderColor=F.color,H.style.color=F.color,x[D]&&P(H,D,12),H.createSpan({text:F.name})}let n=s.createDiv("as-toolbar-right"),r=n.createEl("button",{cls:"as-toolbar-btn",attr:{"aria-label":"Toggle favorite"}});(0,y.setIcon)(r,t.isFavorite?"star":"star-off"),r.addEventListener("click",()=>{this.store.toggleFavorite(t.id,this.settings),this.saveSettings(),this.render()});let o=n.createEl("button",{cls:"as-toolbar-btn",attr:{"aria-label":this.isEditing?"Preview":"Edit"}});(0,y.setIcon)(o,this.isEditing?"eye":"pencil"),o.addEventListener("click",()=>{this.isEditing=!this.isEditing,this.render()});let l=n.createEl("button",{cls:"as-toolbar-btn",attr:{"aria-label":"Open in Finder"}});(0,y.setIcon)(l,"folder-open"),l.addEventListener("click",()=>{ot.shell.showItemInFolder(t.filePath)});let d=e.createDiv("as-detail-meta-bar"),p=_t(t.content),St=t.content.length;if(d.createSpan({cls:"as-meta-item",text:Lt(t.fileSize)}),d.createSpan({cls:"as-meta-item",text:`${nt(St)} chars`}),d.createSpan({cls:"as-meta-item",text:`~${nt(p)} tokens`}),d.createSpan({cls:"as-meta-item",text:Mt(t.lastModified)}),d.createSpan({cls:"as-meta-item as-meta-type",text:t.type}),t.usage&&t.usage.uses>0){let D=e.createDiv("as-detail-usage-bar");D.createSpan({cls:"as-usage-stat",text:`${t.usage.uses} uses`}),D.createSpan({cls:"as-usage-stat",text:`last: ${at(t.usage.lastUsed)}`}),t.usage.isStale&&D.createSpan({cls:"as-badge-stale",text:"stale"}),t.usage.isHeavy&&D.createSpan({cls:"as-badge-heavy",text:"heavy"})}}renderFrontmatter(t,e){let s=Object.keys(e.frontmatter),i=t.createDiv("as-frontmatter");if(e.filePath){let n=i.createDiv("as-fm-prop");n.createSpan({cls:"as-fm-key",text:"path"}),n.createSpan({cls:"as-fm-value",text:e.filePath})}if(!(s.length===0&&!e.filePath))for(let n of s){let r=e.frontmatter[n];if(r==null)continue;let o=i.createDiv("as-fm-prop");o.createSpan({cls:"as-fm-key",text:n});let l=typeof r=="object"?JSON.stringify(r):String(r);l.length>200?o.createDiv({cls:"as-fm-value-long",text:l}):o.createSpan({cls:"as-fm-value",text:l})}}renderPreview(t){let e=this.containerEl.createDiv("as-detail-body");this.renderFrontmatter(e,t);let s=e.createDiv("as-detail-preview markdown-rendered");y.MarkdownRenderer.render(this.app,t.content,s,t.filePath,this)}renderEditor(t){let e=this.containerEl.createDiv("as-detail-body as-detail-body-editor"),s=e.createEl("textarea",{cls:"as-editor-textarea"});s.value=t.content,s.spellcheck=!1,s.addEventListener("keydown",r=>{if((r.metaKey||r.ctrlKey)&&r.key==="s"&&(r.preventDefault(),this.saveFile(t,s.value)),r.key==="Tab"){r.preventDefault();let o=s.selectionStart,l=s.selectionEnd;s.value=s.value.substring(0,o)+" "+s.value.substring(l),s.selectionStart=s.selectionEnd=o+1}});let i=e.createDiv("as-save-bar");i.createEl("button",{cls:"as-save-btn",text:"Save"}).addEventListener("click",()=>{this.saveFile(t,s.value)}),i.createSpan({cls:"as-save-hint",text:"Cmd+S to save"})}saveFile(t,e){try{(0,rt.writeFileSync)(t.filePath,e,"utf-8"),t.content=e,new y.Notice(`Saved ${t.name}`)}catch(s){new y.Notice(`Failed to save: ${s instanceof Error?s.message:String(s)}`)}}};var G=require("obsidian"),lt=require("electron");function Ft(){let a=I("stats"),t=I("health"),e=I("burn"),s=I("context");return{stats:a,health:t,burn:e&&e.length>0?e[0]:null,context:s}}var O=class{containerEl;constructor(t){this.containerEl=t}render(){if(this.containerEl.empty(),this.containerEl.addClass("as-dashboard"),!T()){this.renderNoSkillkit();return}let t=this.containerEl.createDiv("as-dash-loading"),e=t.createDiv("as-dash-spinner");t.createDiv({cls:"as-dash-loading-text",text:"Loading analytics..."}),setTimeout(()=>{let s=Ft();if(t.remove(),s.stats&&this.renderOverview(s.stats,s.health),s.stats&&this.renderTopSkills(s.stats),s.health||s.context){let i=this.containerEl.createDiv("as-dash-row");s.health&&this.renderHealth(s.health,i),s.context&&this.renderContext(s.context,i)}s.burn&&this.renderBurn(s.burn),s.health&&this.renderStale(s.health)},10)}renderNoSkillkit(){let t=this.containerEl.createDiv("as-dash-empty"),e=t.createDiv("as-dash-empty-icon");(0,G.setIcon)(e,"bar-chart-2"),t.createEl("h3",{text:"Dashboard requires skillkit"}),t.createEl("p",{text:"Install skillkit to unlock usage analytics, burn rate, context tax, and more."}),t.createDiv("as-dash-install-cmd").createEl("code",{text:"npm i -g @crafter/skillkit && skillkit scan"}),t.createEl("a",{cls:"as-skillkit-link",text:"Learn more",href:"https://www.npmjs.com/package/@crafter/skillkit"}).addEventListener("click",n=>{n.preventDefault(),lt.shell.openExternal("https://www.npmjs.com/package/@crafter/skillkit")})}renderOverview(t,e){let s=this.containerEl.createDiv("as-dash-section");s.createDiv({cls:"as-dash-title",text:"Overview"});let i=s.createDiv("as-dash-stats");this.statCard(i,String(t.total_invocations),"invocations","activity"),this.statCard(i,String(t.unique_skills),"active skills","sparkles"),this.statCard(i,String(e?.installed??0),"installed","package"),this.statCard(i,String(e?.usage.unused_30d??0),"stale","alert-triangle")}statCard(t,e,s,i){let n=t.createDiv("as-stat-card"),r=n.createDiv("as-stat-icon");(0,G.setIcon)(r,i),n.createDiv({cls:"as-stat-value",text:e}),n.createDiv({cls:"as-stat-label",text:s})}renderTopSkills(t){if(t.top_skills.length===0)return;let e=this.containerEl.createDiv("as-dash-section");e.createDiv({cls:"as-dash-title",text:`Top Skills (${t.period.days}d)`});let s=t.top_skills[0]?.total||1,i=e.createDiv("as-dash-bars");for(let n of t.top_skills.slice(0,10)){let r=i.createDiv("as-bar-row");r.createSpan({cls:"as-bar-name",text:n.name});let l=r.createDiv("as-bar-wrap").createDiv("as-bar-fill");l.style.width=`${n.total/s*100}%`,r.createSpan({cls:"as-bar-count",text:String(n.total)})}}renderHealth(t,e){let s=(e||this.containerEl).createDiv("as-dash-section");s.createDiv({cls:"as-dash-title",text:"Health"});let i=t.usage.used_30d+t.usage.unused_30d,n=i>0?Math.round(t.usage.used_30d/i*100):0,r=s.createDiv("as-dash-health-row"),o=r.createDiv("as-donut");o.style.setProperty("--pct",`${n}`),o.createDiv({cls:"as-donut-label",text:`${n}%`}),o.createDiv({cls:"as-donut-sub",text:"active"});let l=r.createDiv("as-health-details");l.createDiv({cls:"as-health-line",text:`${t.usage.used_30d} used in 30d`}),l.createDiv({cls:"as-health-line as-health-warn",text:`${t.usage.unused_30d} never triggered`});let p=l.createDiv("as-budget-bar").createDiv("as-budget-fill");p.style.width=`${t.metadata.pct}%`,t.metadata.pct>80&&p.addClass("as-budget-over"),l.createDiv({cls:"as-health-line",text:`Metadata budget: ${t.metadata.pct}%`})}renderBurn(t){let e=this.containerEl.createDiv("as-dash-section");e.createDiv({cls:"as-dash-title",text:`Burn Rate \u2014 ${t.agent} (${t.period.days}d)`});let s=e.createDiv("as-dash-stats as-dash-stats-sm");if(this.statCard(s,`$${t.cost.total.toFixed(0)}`,"total cost","flame"),this.statCard(s,`$${(t.cost.total/(t.period.days||1)).toFixed(0)}`,"daily avg","trending-up"),this.statCard(s,`${(t.period.sessions||0).toLocaleString()}`,"sessions","terminal"),this.statCard(s,`${((t.period.api_calls||0)/1e3).toFixed(0)}k`,"API calls","zap"),t.by_model&&t.by_model.length>0){let o=e.createDiv("as-model-breakdown");for(let l of t.by_model.slice(0,4)){let d=o.createDiv("as-model-row");d.createSpan({cls:"as-model-name",text:l.model}),d.createSpan({cls:"as-model-calls",text:`${l.apiCalls.toLocaleString()} calls`}),l.costUsd>0&&d.createSpan({cls:"as-model-cost",text:`$${l.costUsd.toFixed(0)}`})}}let i=t.by_day.slice(-14);if(i.length===0)return;let n=Math.max(...i.map(o=>o.costUsd),1),r=e.createDiv("as-burn-chart");for(let o of i){let l=r.createDiv("as-burn-col"),d=l.createDiv("as-burn-bar"),p=Math.max(2,o.costUsd/n*100);d.style.height=`${p}%`,d.title=`${o.date}: $${o.costUsd.toFixed(0)}`,l.createDiv({cls:"as-burn-date",text:o.date.slice(8)})}}renderContext(t,e){let s=(e||this.containerEl).createDiv("as-dash-section");s.createDiv({cls:"as-dash-title",text:"Context Tax"});let i=t.always_loaded.total_tokens,n=[{label:"CLAUDE.md",tokens:t.always_loaded.claude_md_tokens,cls:"as-ctx-claude"},{label:"Skills metadata",tokens:t.always_loaded.skill_metadata_tokens,cls:"as-ctx-skills"},{label:"Memory",tokens:t.always_loaded.memory_tokens,cls:"as-ctx-memory"}],r=s.createDiv("as-ctx-bar");for(let d of n){let p=r.createDiv(`as-ctx-part ${d.cls}`);p.style.width=`${d.tokens/i*100}%`,p.title=`${d.label}: ${(d.tokens/1e3).toFixed(1)}k tokens`}let o=s.createDiv("as-ctx-legend");for(let d of n){let p=o.createDiv("as-ctx-legend-item");p.createSpan({cls:`as-ctx-dot ${d.cls}`}),p.createSpan({text:`${d.label}: ${(d.tokens/1e3).toFixed(1)}k`})}let l=s.createDiv("as-ctx-costs");l.createDiv({text:`Per session (cached): $${t.session_estimate.with_cache.toFixed(2)}`}),l.createDiv({text:`Without cache: $${t.session_estimate.without_cache.toFixed(2)}`}),l.createDiv({cls:"as-ctx-savings",text:`Cache saves ${t.session_estimate.savings_pct.toFixed(0)}%`})}renderStale(t){if(t.usage.never_used.length===0)return;let e=this.containerEl.createDiv("as-dash-section");e.createDiv({cls:"as-dash-title",text:`Stale Skills (${t.usage.unused_30d})`});let s=e.createDiv("as-stale-list");for(let n of t.usage.never_used.slice(0,20))s.createDiv("as-stale-item").createSpan({text:n});t.usage.never_used.length>20&&s.createDiv({cls:"as-stale-more",text:`+${t.usage.never_used.length-20} more`});let i=e.createDiv("as-stale-hint");i.createEl("code",{text:"skillkit prune"}),i.createSpan({text:" to remove unused skills"})}};var L="agentfiles-view",N=class extends ct.ItemView{store;settings;saveSettings;sidebarPanel;listPanel;detailPanel;dashboardPanel;sidebarEl;listEl;detailEl;dashboardEl;isDashboard=!1;updateRef=null;constructor(t,e,s,i){super(t),this.store=e,this.settings=s,this.saveSettings=i}getViewType(){return L}getDisplayText(){return"Agentfiles"}getIcon(){return"cpu"}onOpen(){let t=this.contentEl;t.empty(),t.addClass("as-container"),this.sidebarEl=t.createDiv("as-panel as-panel-sidebar"),this.listEl=t.createDiv("as-panel as-panel-list"),this.detailEl=t.createDiv("as-panel as-panel-detail"),this.dashboardEl=t.createDiv("as-panel as-panel-dashboard"),this.dashboardEl.style.display="none",this.sidebarPanel=new j(this.sidebarEl,this.store,()=>this.toggleDashboard()),this.listPanel=new A(this.listEl,this.store,e=>this.onSelectItem(e)),this.detailPanel=new $(this.detailEl,this.store,this.settings,this.saveSettings,this),this.dashboardPanel=new O(this.dashboardEl),this.updateRef=this.store.on("updated",()=>this.renderAll()),this.renderAll()}toggleDashboard(){this.isDashboard=!this.isDashboard,this.isDashboard?(this.listEl.style.display="none",this.detailEl.style.display="none",this.dashboardEl.style.display="block",this.dashboardPanel.render()):(this.listEl.style.display="",this.detailEl.style.display="",this.dashboardEl.style.display="none"),this.sidebarPanel.setDashboardActive(this.isDashboard),this.sidebarPanel.render()}renderAll(){this.sidebarPanel.render(),this.isDashboard||(this.listPanel.render(),this.store.filteredItems.length||this.detailPanel.clear())}onSelectItem(t){this.isDashboard&&this.toggleDashboard(),this.listPanel.setSelected(t.id),this.listPanel.render(),this.detailPanel.show(t)}onClose(){this.updateRef&&this.store.offref(this.updateRef)}};var gt=require("obsidian");var u=require("fs"),m=require("path"),V=require("os"),ht=require("obsidian"),pt=require("crypto");var dt=new Set(["readme.md","license","license.md","changelog.md",".ds_store","thumbs.db"]);function Ht(a){return(0,pt.createHash)("sha256").update(a).digest("hex").slice(0,12)}function jt(a){let t=a.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);if(!t)return{frontmatter:{},content:a};try{let e=(0,ht.parseYaml)(t[1]);return{frontmatter:typeof e=="object"&&e?e:{},content:t[2]}}catch{return{frontmatter:{},content:a}}}function At(a,t,e,s){if(typeof a.name=="string"&&a.name)return a.name;let i=(0,m.basename)(e,(0,m.extname)(e));if(i==="SKILL")return(0,m.basename)((0,m.join)(e,".."));if(s==="flat-md"||s==="mdc")return i;let n=t.match(/^#\s+(.+)$/m);return n?n[1].trim():i}function $t(a,t,e){if(!(0,u.existsSync)(a))return[];let s=[];for(let i of(0,u.readdirSync)(a,{withFileTypes:!0})){if(!i.isDirectory()&&!i.isSymbolicLink())continue;let n=(0,m.join)(a,i.name,"SKILL.md");if(!(0,u.existsSync)(n))continue;let r=M(n,t,e);r&&s.push(r)}return s}function Ot(a,t,e){if(!(0,u.existsSync)(a))return[];let s=[];for(let i of(0,u.readdirSync)(a,{withFileTypes:!0})){if(i.isDirectory()||i.isSymbolicLink()){let o=(0,m.join)(a,i.name,"SKILL.md");if((0,u.existsSync)(o)){let p=M(o,t,e);p&&s.push(p);continue}let l=(0,u.readdirSync)((0,m.join)(a,i.name)).filter(p=>p.endsWith(".md")&&!dt.has(p.toLowerCase())),d=l.find(p=>p.toLowerCase()===`${i.name.toLowerCase()}.md`)||l[0];if(d){let p=M((0,m.join)(a,i.name,d),t,e);p&&s.push(p)}continue}let n=i.name.toLowerCase();if(!n.endsWith(".md")||dt.has(n))continue;let r=M((0,m.join)(a,i.name),t,e,"flat-md");r&&s.push(r)}return s}function Nt(a,t,e){if(!(0,u.existsSync)(a))return[];let s=[];for(let i of(0,u.readdirSync)(a,{withFileTypes:!0})){if(!i.name.endsWith(".mdc")&&!i.name.endsWith(".md")||i.isDirectory())continue;let n=M((0,m.join)(a,i.name),t,e,"mdc");n&&s.push(n)}return s}function M(a,t,e,s="directory-with-skillmd"){try{let i=(0,u.readFileSync)(a,"utf-8"),n=(0,u.statSync)(a),{frontmatter:r,content:o}=jt(i),l=At(r,o,a,s),d=typeof r.description=="string"?r.description:"",p;try{p=(0,u.realpathSync)(a)}catch{p=a}return{id:Ht(p),name:l,description:d,type:t,tools:[e],filePath:a,realPath:p,dirPath:(0,m.join)(a,".."),content:i,frontmatter:r,lastModified:n.mtimeMs,fileSize:n.size,isFavorite:!1,collections:[]}}catch{return null}}function Z(a,t){switch(a.pattern){case"directory-with-skillmd":return $t(a.baseDir,a.type,t);case"flat-md":return Ot(a.baseDir,a.type,t);case"mdc":return Nt(a.baseDir,a.type,t)}}function Vt(a,t){let e=[],s=[{sub:".claude/skills",type:"skill",pattern:"directory-with-skillmd"},{sub:".claude/commands",type:"command",pattern:"flat-md"},{sub:".claude/agents",type:"agent",pattern:"flat-md"},{sub:".cursor/skills",type:"skill",pattern:"directory-with-skillmd"},{sub:".codex/skills",type:"skill",pattern:"directory-with-skillmd"}];for(let i of s){let n=(0,m.join)(a,i.sub);if(!(0,u.existsSync)(n))continue;let r={baseDir:n,type:i.type,pattern:i.pattern};e.push(...Z(r,t))}return e}function ut(a){return a.projectsHomeDir||(0,V.homedir)()}function Wt(a,t){let e=(0,V.homedir)(),s=[];for(let i of[...t.paths,...t.agentPaths]){let n=(0,m.relative)(e,i.baseDir);if(n.startsWith("..")||n.startsWith("/"))continue;let r=(0,m.join)(a,n);if((0,u.existsSync)(r))try{s.push(...Z({...i,baseDir:r},t.id))}catch{}}return s}function Bt(a){let t=ut(a);if(!(0,u.existsSync)(t))return[];let e=[];try{let s=new Set(["node_modules",".Trash","Library","Applications","Music","Movies","Pictures","Public"]);for(let i of(0,u.readdirSync)(t,{withFileTypes:!0})){if(!i.isDirectory()&&!i.isSymbolicLink()||s.has(i.name))continue;let n=(0,m.join)(t,i.name);for(let r of v){if(!r.isInstalled())continue;let o=a.tools[r.id];if(o&&!o.enabled)continue;let l=Wt(n,r);l.length>0&&e.push({items:l,toolId:r.id})}}}catch{}return e}function K(a,t){let e=t||(0,V.homedir)();if(!a.startsWith(e+"/"))return"global";let i=a.slice(e.length+1).split("/");return i.length>1&&!i[0].startsWith(".")?i[0]:"global"}function mt(a){let t=new Map,e=new Map;function s(i,n){let r=t.get(i.id);if(r){r.tools.includes(n)||r.tools.push(n);return}let o=e.get(i.name);if(o){let l=t.get(o);l&&!l.tools.includes(n)&&l.tools.push(n);return}i.isFavorite=a.favorites.includes(i.id);for(let[l,d]of Object.entries(a.collections))d.includes(i.id)&&i.collections.push(l);t.set(i.id,i),e.set(i.name,i.id)}for(let i of v){if(!i.isInstalled())continue;let n=a.tools[i.id];if(n&&!n.enabled)continue;let r=[...i.paths,...i.agentPaths];for(let o of r)for(let l of Z(o,i.id))s(l,i.id)}for(let i of a.customScanPaths)if((0,u.existsSync)(i))for(let n of Vt(i,"claude-code"))s(n,"claude-code");if(a.projectScanEnabled)for(let{items:i,toolId:n}of Bt(a))for(let r of i)s(r,n);return t}function ft(a){let t=[];for(let e of v)if(e.isInstalled())for(let s of[...e.paths,...e.agentPaths])(0,u.existsSync)(s.baseDir)&&t.push(s.baseDir);if(a?.projectScanEnabled){let e=ut(a),s=new Set(["node_modules",".Trash","Library","Applications","Music","Movies","Pictures","Public"]);try{for(let i of(0,u.readdirSync)(e,{withFileTypes:!0})){if(!i.isDirectory()&&!i.isSymbolicLink()||s.has(i.name))continue;let n=(0,m.join)(e,i.name);for(let r of[".claude/skills",".claude/commands",".claude/agents",".cursor/skills",".codex/skills"]){let o=(0,m.join)(n,r);(0,u.existsSync)(o)&&t.push(o)}}}catch{}}return t}var W=class extends gt.Events{items=new Map;_filter={kind:"all"};_searchQuery="";_projectsHomeDir="";get filter(){return this._filter}get searchQuery(){return this._searchQuery}get allItems(){return Array.from(this.items.values())}get filteredItems(){let t=this.allItems;switch(this._filter.kind){case"favorites":t=t.filter(e=>e.isFavorite);break;case"tool":t=t.filter(e=>e.tools.includes(this._filter.toolId));break;case"type":t=t.filter(e=>e.type===this._filter.type);break;case"collection":t=t.filter(e=>e.collections.includes(this._filter.name));break;case"project":t=t.filter(e=>K(e.filePath,this._projectsHomeDir)===this._filter.project);break}if(this._searchQuery){let e=this._searchQuery.toLowerCase();t=t.filter(s=>s.name.toLowerCase().includes(e)||s.description.toLowerCase().includes(e)||s.content.toLowerCase().includes(e))}return t.sort((e,s)=>e.name.localeCompare(s.name))}getItem(t){return this.items.get(t)}get hasSkillkit(){return T()}refresh(t){this._projectsHomeDir=t.projectsHomeDir,this.items=mt(t),this.enrichWithSkillkit(),this.trigger("updated")}enrichWithSkillkit(){if(!T())return;let t=it();for(let e of this.items.values()){let s=e.filePath.split("/").slice(-2,-1)[0],i=e.name.toLowerCase().replace(/\s+/g,"-"),n=t.get(e.name)||t.get(s)||t.get(i);n?(n.isHeavy=e.content.length>5e3,e.usage=n):e.usage={uses:0,lastUsed:null,daysSinceUsed:null,isStale:!1,isHeavy:e.content.length>5e3}}}setFilter(t){this._filter=t,this.trigger("updated")}setSearch(t){this._searchQuery=t,this.trigger("updated")}toggleFavorite(t,e){let s=this.items.get(t);s&&(s.isFavorite=!s.isFavorite,s.isFavorite?e.favorites.includes(t)||e.favorites.push(t):e.favorites=e.favorites.filter(i=>i!==t),this.trigger("updated"))}getToolCounts(){let t=new Map;for(let e of this.items.values())for(let s of e.tools)t.set(s,(t.get(s)||0)+1);return t}getTypeCounts(){let t=new Map;for(let e of this.items.values())t.set(e.type,(t.get(e.type)||0)+1);return t}getProjectCounts(){let t=new Map;for(let e of this.items.values()){let s=K(e.filePath,this._projectsHomeDir);t.set(s,(t.get(s)||0)+1)}return t}};var vt=require("fs"),B=class{watchers=[];debounceTimer=null;debounceMs;onChange;constructor(t,e){this.debounceMs=t,this.onChange=e}watchPaths(t){this.close();for(let e of t)try{let s=(0,vt.watch)(e,{recursive:!0},()=>this.scheduleUpdate());this.watchers.push(s)}catch{}}scheduleUpdate(){this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.onChange()},this.debounceMs)}close(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null);for(let t of this.watchers)try{t.close()}catch{}this.watchers=[]}};var b=require("obsidian");var U=class extends b.PluginSettingTab{plugin;constructor(t,e){super(t,e),this.plugin=e}display(){let{containerEl:t}=this;t.empty(),new b.Setting(t).setName("Agentfiles").setHeading(),new b.Setting(t).setName("File watching").setDesc("Automatically detect changes to skill files").addToggle(e=>e.setValue(this.plugin.settings.watchEnabled).onChange(async s=>{this.plugin.settings.watchEnabled=s,await this.plugin.saveSettings(),this.plugin.restartWatcher()})),new b.Setting(t).setName("Watch debounce (ms)").setDesc("Delay before re-scanning after file changes").addText(e=>e.setValue(String(this.plugin.settings.watchDebounceMs)).onChange(async s=>{let i=parseInt(s);!isNaN(i)&&i>=100&&(this.plugin.settings.watchDebounceMs=i,await this.plugin.saveSettings())})),new b.Setting(t).setName("Project scanning").setHeading(),new b.Setting(t).setName("Enable project scanning").setDesc("Scan all directories under the projects home folder for project-level skills").addToggle(e=>e.setValue(this.plugin.settings.projectScanEnabled).onChange(async s=>{this.plugin.settings.projectScanEnabled=s,await this.plugin.saveSettings(),this.plugin.refreshStore(),this.plugin.restartWatcher()})),new b.Setting(t).setName("Projects home directory").setDesc("Root directory to scan for project-level skills. Leave empty for home directory (~).").addText(e=>e.setPlaceholder("~").setValue(this.plugin.settings.projectsHomeDir).onChange(async s=>{this.plugin.settings.projectsHomeDir=s,await this.plugin.saveSettings(),this.plugin.refreshStore(),this.plugin.restartWatcher()})),new b.Setting(t).setName("Tools").setHeading();for(let e of v){let s=e.isInstalled(),i=this.plugin.settings.tools[e.id]||{enabled:!0,customPaths:[]};new b.Setting(t).setName(e.name).setDesc(s?"Installed":"Not detected").addToggle(n=>n.setValue(s&&i.enabled).setDisabled(!s).onChange(async r=>{this.plugin.settings.tools[e.id]={...i,enabled:r},await this.plugin.saveSettings(),this.plugin.refreshStore()}))}}};var Q={tools:{},watchEnabled:!0,watchDebounceMs:500,favorites:[],collections:{},customScanPaths:[],projectScanEnabled:!0,projectsHomeDir:""};var R=class extends bt.Plugin{settings=Q;store=new W;watcher=null;async onload(){await this.loadSettings(),this.addVaultPath(),this.registerView(L,t=>new N(t,this.store,this.settings,()=>this.saveSettings())),this.addRibbonIcon("cpu","Agentfiles",()=>this.activateView()),this.addCommand({id:"open",name:"Open",callback:()=>this.activateView()}),this.addSettingTab(new U(this.app,this)),this.refreshStore(),this.startWatcher()}addVaultPath(){let t=this.app.vault.adapter;if(!t.getBasePath)return;let e=t.getBasePath();this.settings.customScanPaths.includes(e)||this.settings.customScanPaths.push(e)}onunload(){this.stopWatcher()}refreshStore(){this.store.refresh(this.settings)}startWatcher(){this.settings.watchEnabled&&(this.watcher=new B(this.settings.watchDebounceMs,()=>this.refreshStore()),this.watcher.watchPaths(ft(this.settings)))}stopWatcher(){this.watcher&&(this.watcher.close(),this.watcher=null)}restartWatcher(){this.stopWatcher(),this.startWatcher()}async activateView(){let t=this.app.workspace.getLeavesOfType(L);if(t.length>0){this.app.workspace.revealLeaf(t[0]);return}let e=this.app.workspace.getLeaf("tab");await e.setViewState({type:L,active:!0}),this.app.workspace.revealLeaf(e)}async loadSettings(){this.settings=Object.assign({},Q,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}}; diff --git a/src/main.ts b/src/main.ts index 86300a3..e852b64 100644 --- a/src/main.ts +++ b/src/main.ts @@ -60,7 +60,7 @@ export default class AgentfilesPlugin extends Plugin { this.watcher = new SkillWatcher(this.settings.watchDebounceMs, () => this.refreshStore() ); - this.watcher.watchPaths(getWatchPaths()); + this.watcher.watchPaths(getWatchPaths(this.settings)); } stopWatcher(): void { diff --git a/src/scanner.ts b/src/scanner.ts index 7f87953..735760c 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -5,11 +5,12 @@ import { realpathSync, statSync, } from "fs"; -import { join, basename, extname } from "path"; +import { join, basename, extname, relative } from "path"; +import { homedir } from "os"; import { parseYaml } from "obsidian"; import { createHash } from "crypto"; import { TOOL_CONFIGS } from "./tool-configs"; -import type { SkillItem, SkillPath, SkillType, ChopsSettings } from "./types"; +import type { SkillItem, SkillPath, SkillType, ChopsSettings, ToolConfig } from "./types"; const IGNORED_FILES = new Set([ "readme.md", @@ -211,6 +212,64 @@ function scanProjectSkills(projectRoot: string, toolId: string): SkillItem[] { return results; } +function getProjectsHomeDir(settings: ChopsSettings): string { + return settings.projectsHomeDir || homedir(); +} + +function scanToolProjectPaths( + projectRoot: string, + tool: ToolConfig +): SkillItem[] { + const home = homedir(); + const results: SkillItem[] = []; + for (const sp of [...tool.paths, ...tool.agentPaths]) { + const rel = relative(home, sp.baseDir); + if (rel.startsWith("..") || rel.startsWith("/")) continue; + const projectPath = join(projectRoot, rel); + if (!existsSync(projectPath)) continue; + try { + results.push(...scanPath({ ...sp, baseDir: projectPath }, tool.id)); + } catch { /* permission errors, broken symlinks, etc */ } + } + return results; +} + +function scanProjectRoots(settings: ChopsSettings): { items: SkillItem[]; toolId: string }[] { + const homeDir = getProjectsHomeDir(settings); + if (!existsSync(homeDir)) return []; + + const results: { items: SkillItem[]; toolId: string }[] = []; + try { + const SKIP_DIRS = new Set(["node_modules", ".Trash", "Library", "Applications", "Music", "Movies", "Pictures", "Public"]); + for (const entry of readdirSync(homeDir, { withFileTypes: true })) { + if (!entry.isDirectory() && !entry.isSymbolicLink()) continue; + if (SKIP_DIRS.has(entry.name)) continue; + const projectPath = join(homeDir, entry.name); + for (const tool of TOOL_CONFIGS) { + if (!tool.isInstalled()) continue; + const toolSettings = settings.tools[tool.id]; + if (toolSettings && !toolSettings.enabled) continue; + const items = scanToolProjectPaths(projectPath, tool); + if (items.length > 0) { + results.push({ items, toolId: tool.id }); + } + } + } + } catch { /* permission errors, etc */ } + return results; +} + +export function getProjectName(filePath: string, projectsHomeDir: string): string { + const homeDir = projectsHomeDir || homedir(); + if (!filePath.startsWith(homeDir + "/")) return "global"; + const rest = filePath.slice(homeDir.length + 1); + const parts = rest.split("/"); + if (parts.length > 1 && !parts[0].startsWith(".")) { + return parts[0]; + } + return "global"; +} + export function scanAll(settings: ChopsSettings): Map { const items = new Map(); const nameMap = new Map(); @@ -263,6 +322,14 @@ export function scanAll(settings: ChopsSettings): Map { } } + if (settings.projectScanEnabled) { + for (const { items: projectItems, toolId } of scanProjectRoots(settings)) { + for (const item of projectItems) { + addItem(item, toolId); + } + } + } + return items; } @@ -270,7 +337,7 @@ export function getInstalledTools(): string[] { return TOOL_CONFIGS.filter((t) => t.isInstalled()).map((t) => t.id); } -export function getWatchPaths(): string[] { +export function getWatchPaths(settings?: ChopsSettings): string[] { const paths: string[] = []; for (const tool of TOOL_CONFIGS) { if (!tool.isInstalled()) continue; @@ -280,5 +347,19 @@ export function getWatchPaths(): string[] { } } } + if (settings?.projectScanEnabled) { + const homeDir = getProjectsHomeDir(settings); + const SKIP_DIRS = new Set(["node_modules", ".Trash", "Library", "Applications", "Music", "Movies", "Pictures", "Public"]); + try { + for (const entry of readdirSync(homeDir, { withFileTypes: true })) { + if ((!entry.isDirectory() && !entry.isSymbolicLink()) || SKIP_DIRS.has(entry.name)) continue; + const projectPath = join(homeDir, entry.name); + for (const dir of [".claude/skills", ".claude/commands", ".claude/agents", ".cursor/skills", ".codex/skills"]) { + const fullPath = join(projectPath, dir); + if (existsSync(fullPath)) paths.push(fullPath); + } + } + } catch { /* permission errors */ } + } return paths; } diff --git a/src/settings.ts b/src/settings.ts index e951f0d..868cb73 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -44,6 +44,41 @@ export class AgentfilesSettingTab extends PluginSettingTab { }) ); + new Setting(containerEl).setName("Project scanning").setHeading(); + + new Setting(containerEl) + .setName("Enable project scanning") + .setDesc( + "Scan all directories under the projects home folder for project-level skills" + ) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.projectScanEnabled) + .onChange(async (value) => { + this.plugin.settings.projectScanEnabled = value; + await this.plugin.saveSettings(); + this.plugin.refreshStore(); + this.plugin.restartWatcher(); + }) + ); + + new Setting(containerEl) + .setName("Projects home directory") + .setDesc( + "Root directory to scan for project-level skills. Leave empty for home directory (~)." + ) + .addText((text) => + text + .setPlaceholder("~") + .setValue(this.plugin.settings.projectsHomeDir) + .onChange(async (value) => { + this.plugin.settings.projectsHomeDir = value; + await this.plugin.saveSettings(); + this.plugin.refreshStore(); + this.plugin.restartWatcher(); + }) + ); + new Setting(containerEl).setName("Tools").setHeading(); for (const tool of TOOL_CONFIGS) { diff --git a/src/store.ts b/src/store.ts index 974d8ac..28496c7 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,12 +1,13 @@ import { Events } from "obsidian"; import type { SkillItem, SidebarFilter, ChopsSettings } from "./types"; -import { scanAll } from "./scanner"; +import { scanAll, getProjectName } from "./scanner"; import { getSkillkitStats, isSkillkitAvailable } from "./skillkit"; export class SkillStore extends Events { private items: Map = new Map(); private _filter: SidebarFilter = { kind: "all" }; private _searchQuery = ""; + private _projectsHomeDir = ""; get filter(): SidebarFilter { return this._filter; @@ -40,6 +41,11 @@ export class SkillStore extends Events { i.collections.includes(this._filter.name) ); break; + case "project": + result = result.filter( + (i) => getProjectName(i.filePath, this._projectsHomeDir) === this._filter.project + ); + break; } if (this._searchQuery) { @@ -64,6 +70,7 @@ export class SkillStore extends Events { } refresh(settings: ChopsSettings): void { + this._projectsHomeDir = settings.projectsHomeDir; this.items = scanAll(settings); this.enrichWithSkillkit(); this.trigger("updated"); @@ -132,4 +139,13 @@ export class SkillStore extends Events { } return counts; } + + getProjectCounts(): Map { + const counts = new Map(); + for (const item of this.items.values()) { + const project = getProjectName(item.filePath, this._projectsHomeDir); + counts.set(project, (counts.get(project) || 0) + 1); + } + return counts; + } } diff --git a/src/types.ts b/src/types.ts index 4c1b7ae..ff96693 100644 --- a/src/types.ts +++ b/src/types.ts @@ -47,7 +47,8 @@ export type SidebarFilter = | { kind: "favorites" } | { kind: "tool"; toolId: string } | { kind: "type"; type: SkillType } - | { kind: "collection"; name: string }; + | { kind: "collection"; name: string } + | { kind: "project"; project: string }; export interface ChopsSettings { tools: Record; @@ -56,6 +57,8 @@ export interface ChopsSettings { favorites: string[]; collections: Record; customScanPaths: string[]; + projectScanEnabled: boolean; + projectsHomeDir: string; } export const DEFAULT_SETTINGS: ChopsSettings = { @@ -65,4 +68,6 @@ export const DEFAULT_SETTINGS: ChopsSettings = { favorites: [], collections: {}, customScanPaths: [], + projectScanEnabled: true, + projectsHomeDir: "", }; diff --git a/src/views/detail.ts b/src/views/detail.ts index e6556c0..8f4b618 100644 --- a/src/views/detail.ts +++ b/src/views/detail.ts @@ -163,10 +163,16 @@ export class DetailPanel { private renderFrontmatter(container: HTMLElement, item: SkillItem): void { const keys = Object.keys(item.frontmatter); - if (keys.length === 0) return; - const section = container.createDiv("as-frontmatter"); + if (item.filePath) { + const pathProp = section.createDiv("as-fm-prop"); + pathProp.createSpan({ cls: "as-fm-key", text: "path" }); + pathProp.createSpan({ cls: "as-fm-value", text: item.filePath }); + } + + if (keys.length === 0 && !item.filePath) return; + for (const key of keys) { const value = item.frontmatter[key]; if (value === undefined || value === null) continue; diff --git a/src/views/sidebar.ts b/src/views/sidebar.ts index 67c7906..24a88da 100644 --- a/src/views/sidebar.ts +++ b/src/views/sidebar.ts @@ -28,6 +28,7 @@ export class SidebarPanel { this.renderLibrarySection(); this.renderTypeSection(); this.renderToolSection(); + this.renderProjectSection(); this.renderCollectionSection(); if (!this.store.hasSkillkit) { @@ -123,6 +124,23 @@ export class SidebarPanel { } } + private renderProjectSection(): void { + const projectCounts = this.store.getProjectCounts(); + if (projectCounts.size === 0) return; + + const items: { label: string; icon: string; filter: SidebarFilter; count: number }[] = []; + for (const [project, count] of projectCounts) { + items.push({ + label: project, + icon: "folder-git-2", + filter: { kind: "project", project }, + count, + }); + } + items.sort((a, b) => a.label.localeCompare(b.label)); + this.renderSection("Projects", items); + } + private renderCollectionSection(): void { const section = this.containerEl.createDiv("as-sidebar-section"); section.createDiv({ cls: "as-sidebar-title", text: "Collections" }); @@ -226,6 +244,8 @@ export class SidebarPanel { return current.type === filter.type; if (current.kind === "collection" && filter.kind === "collection") return current.name === filter.name; + if (current.kind === "project" && filter.kind === "project") + return current.project === filter.project; return true; } }